| Date: Fri, 16 Aug 2013 12:52:00 -0800 |
| Subject: staging: exfat: add filesystem to drivers/staging/exfat |
| |
| From: Someone <someone@samsung.com> |
| |
| This is the exfat code version 1.2.4 as found on the |
| http://opensource.samsung.com site in the zip file exfat_1.2.4.zip with |
| the contents: |
| $ sha256sum exfat_1.2.4.zip |
| 4cb39c4f44b7a68cc391596384744a32f948ba659697542ff771764b215810f3 exfat_1.2.4.zip |
| |
| Released under the GNU General Public License, version 2, as specified |
| from that web site. |
| |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/staging/exfat/Makefile | 13 + |
| drivers/staging/exfat/README | 21 + |
| drivers/staging/exfat/exfat.c | 4950 +++++++++++++++++++++++++++++++++ |
| drivers/staging/exfat/exfat.h | 598 ++++ |
| drivers/staging/exfat/exfat_api.c | 457 +++ |
| drivers/staging/exfat/exfat_api.h | 165 ++ |
| drivers/staging/exfat/exfat_blkdev.c | 156 ++ |
| drivers/staging/exfat/exfat_blkdev.h | 46 + |
| drivers/staging/exfat/exfat_cache.c | 740 +++++ |
| drivers/staging/exfat/exfat_cache.h | 63 + |
| drivers/staging/exfat/exfat_config.h | 57 + |
| drivers/staging/exfat/exfat_data.c | 40 + |
| drivers/staging/exfat/exfat_data.h | 39 + |
| drivers/staging/exfat/exfat_global.c | 119 + |
| drivers/staging/exfat/exfat_global.h | 127 + |
| drivers/staging/exfat/exfat_nls.c | 347 +++ |
| drivers/staging/exfat/exfat_nls.h | 68 + |
| drivers/staging/exfat/exfat_oal.c | 146 + |
| drivers/staging/exfat/exfat_oal.h | 53 + |
| drivers/staging/exfat/exfat_part.h | 54 + |
| drivers/staging/exfat/exfat_super.c | 2154 ++++++++++++++ |
| drivers/staging/exfat/exfat_super.h | 149 + |
| drivers/staging/exfat/exfat_upcase.c | 390 +++ |
| drivers/staging/exfat/exfat_version.h | 1 + |
| 24 files changed, 10953 insertions(+) |
| create mode 100644 drivers/staging/exfat/Makefile |
| create mode 100644 drivers/staging/exfat/README |
| create mode 100644 drivers/staging/exfat/exfat.c |
| create mode 100644 drivers/staging/exfat/exfat.h |
| create mode 100644 drivers/staging/exfat/exfat_api.c |
| create mode 100644 drivers/staging/exfat/exfat_api.h |
| create mode 100644 drivers/staging/exfat/exfat_blkdev.c |
| create mode 100644 drivers/staging/exfat/exfat_blkdev.h |
| create mode 100644 drivers/staging/exfat/exfat_cache.c |
| create mode 100644 drivers/staging/exfat/exfat_cache.h |
| create mode 100644 drivers/staging/exfat/exfat_config.h |
| create mode 100644 drivers/staging/exfat/exfat_data.c |
| create mode 100644 drivers/staging/exfat/exfat_data.h |
| create mode 100644 drivers/staging/exfat/exfat_global.c |
| create mode 100644 drivers/staging/exfat/exfat_global.h |
| create mode 100644 drivers/staging/exfat/exfat_nls.c |
| create mode 100644 drivers/staging/exfat/exfat_nls.h |
| create mode 100644 drivers/staging/exfat/exfat_oal.c |
| create mode 100644 drivers/staging/exfat/exfat_oal.h |
| create mode 100644 drivers/staging/exfat/exfat_part.h |
| create mode 100644 drivers/staging/exfat/exfat_super.c |
| create mode 100644 drivers/staging/exfat/exfat_super.h |
| create mode 100644 drivers/staging/exfat/exfat_upcase.c |
| create mode 100644 drivers/staging/exfat/exfat_version.h |
| |
| diff --git a/drivers/staging/exfat/Makefile b/drivers/staging/exfat/Makefile |
| new file mode 100644 |
| index 00000000..5ff84c8d |
| --- /dev/null |
| +++ b/drivers/staging/exfat/Makefile |
| @@ -0,0 +1,13 @@ |
| +obj-m += exfat_core.o exfat_fs.o |
| + |
| +exfat_fs-y := exfat_super.o |
| + |
| +exfat_core-y := exfat.o exfat_api.o exfat_blkdev.o exfat_cache.o \ |
| + exfat_data.o exfat_global.o exfat_nls.o exfat_oal.o exfat_upcase.o |
| + |
| +all: |
| + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules |
| + |
| +clean: |
| + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean |
| + |
| diff --git a/drivers/staging/exfat/README b/drivers/staging/exfat/README |
| new file mode 100644 |
| index 00000000..32a1f5f3 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/README |
| @@ -0,0 +1,21 @@ |
| +HOW TO BUILD YOUR KERNEL WITH THE exFAT FILESYSTEM DRIVER |
| + |
| +- get Kernel code of your Samsung Product in OSRC website. (http://opensource.samsung.com/) |
| + |
| +- make "exfat" directory into {Kernel}/drivers/ |
| + |
| +- copy exFAT source code into {Kernel}/drivers/exfat/ |
| + |
| +- edit default kernel configuration file of your device. |
| +$ vi {Kernel}/arch/arm/configs/{your_device_defconfig} |
| +#NLS Setting |
| +CONFIG_NLS_UTF8=y |
| + |
| +- edit a Makefile in Kernel/drivers |
| +$ vi {Kernel}/drivers/Makefile |
| +obj-y += exfat/ |
| + |
| +- execute command to make for your Kernel. (see README_kernel.txt in Kernel) |
| + ex) make arch=arm defconfig |
| + make |
| + |
| diff --git a/drivers/staging/exfat/exfat.c b/drivers/staging/exfat/exfat.c |
| new file mode 100644 |
| index 00000000..c7af0e7d |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat.c |
| @@ -0,0 +1,4950 @@ |
| +/* Some of the source code in this file came from "linux/fs/fat/misc.c". */ |
| +/* |
| + * linux/fs/fat/misc.c |
| + * |
| + * 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. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#include <linux/version.h> |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#include "exfat_data.h" |
| +#include "exfat_oal.h" |
| + |
| +#include "exfat_blkdev.h" |
| +#include "exfat_cache.h" |
| +#include "exfat_nls.h" |
| +#include "exfat_api.h" |
| +#include "exfat_super.h" |
| +#include "exfat.h" |
| + |
| +#include <linux/blkdev.h> |
| + |
| +#define THERE_IS_MBR 0 |
| + |
| +#if (THERE_IS_MBR == 1) |
| +#include "exfat_part.h" |
| +#endif |
| + |
| +#define DELAYED_SYNC 0 |
| + |
| +#define ELAPSED_TIME 0 |
| + |
| +#if (ELAPSED_TIME == 1) |
| +#include <linux/time.h> |
| + |
| +static UINT32 __t1, __t2; |
| +static UINT32 get_current_msec(void) |
| +{ |
| + struct timeval tm; |
| + do_gettimeofday(&tm); |
| + return (UINT32)(tm.tv_sec*1000000 + tm.tv_usec); |
| +} |
| +#define TIME_START() do {__t1 = get_current_msec(); } while (0) |
| +#define TIME_END() do {__t2 = get_current_msec(); } while (0) |
| +#define PRINT_TIME(n) do {printk("[EXFAT] Elapsed time %d = %d (usec)\n", n, (__t2 - __t1)); } while (0) |
| +#else |
| +#define TIME_START() |
| +#define TIME_END() |
| +#define PRINT_TIME(n) |
| +#endif |
| + |
| +static void __set_sb_dirty(struct super_block *sb) |
| +{ |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) |
| + sb->s_dirt = 1; |
| +#else |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + sbi->s_dirt = 1; |
| +#endif |
| +} |
| + |
| +extern UINT8 uni_upcase[]; |
| + |
| +static UINT8 name_buf[MAX_PATH_LENGTH *MAX_CHARSET_SIZE]; |
| + |
| +static INT8 *reserved_names[] = { |
| + "AUX ", "CON ", "NUL ", "PRN ", |
| + "COM1 ", "COM2 ", "COM3 ", "COM4 ", |
| + "COM5 ", "COM6 ", "COM7 ", "COM8 ", "COM9 ", |
| + "LPT1 ", "LPT2 ", "LPT3 ", "LPT4 ", |
| + "LPT5 ", "LPT6 ", "LPT7 ", "LPT8 ", "LPT9 ", |
| + NULL |
| +}; |
| + |
| +static UINT8 free_bit[] = { |
| + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, |
| + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, |
| + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, |
| + 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, |
| + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, |
| + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, |
| + 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, |
| + 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, |
| + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, |
| + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, |
| + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, |
| + 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, |
| + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 |
| +}; |
| + |
| +static UINT8 used_bit[] = { |
| + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, |
| + 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, |
| + 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, |
| + 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, |
| + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, |
| + 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, |
| + 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, |
| + 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, |
| + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, |
| + 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, |
| + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, |
| + 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, |
| + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 |
| +}; |
| + |
| +INT32 ffsInit(void) |
| +{ |
| + INT32 ret; |
| + |
| + ret = bdev_init(); |
| + if (ret) |
| + return ret; |
| + |
| + ret = fs_init(); |
| + if (ret) |
| + return ret; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsShutdown(void) |
| +{ |
| + INT32 ret; |
| + ret = fs_shutdown(); |
| + if (ret) |
| + return ret; |
| + |
| + ret = bdev_shutdown(); |
| + if (ret) |
| + return ret; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsMountVol(struct super_block *sb, INT32 drv) |
| +{ |
| + INT32 i, ret; |
| +#if (THERE_IS_MBR == 1) |
| + MBR_SECTOR_T *p_mbr; |
| + PART_ENTRY_T *p_pte; |
| +#endif |
| + PBR_SECTOR_T *p_pbr; |
| + struct buffer_head *tmp_bh = NULL; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + |
| + printk("[EXFAT] trying to mount...\n"); |
| + |
| + p_fs->drv = drv; |
| + p_fs->dev_ejected = FALSE; |
| + |
| + if (bdev_open(sb)) |
| + return FFS_MEDIAERR; |
| + |
| + if (p_bd->sector_size < sb->s_blocksize) |
| + return FFS_MEDIAERR; |
| + if (p_bd->sector_size > sb->s_blocksize) |
| + sb_set_blocksize(sb, p_bd->sector_size); |
| + |
| + if (sector_read(sb, 0, &tmp_bh, 1) != FFS_SUCCESS) |
| + return FFS_MEDIAERR; |
| + |
| +#if (THERE_IS_MBR == 1) |
| + if (buf[0] != 0xEB) { |
| + p_mbr = (MBR_SECTOR_T *) tmp_bh->b_data; |
| + |
| + if (GET16_A(p_mbr->signature) != MBR_SIGNATURE) { |
| + brelse(tmp_bh); |
| + bdev_close(sb); |
| + return FFS_FORMATERR; |
| + } |
| + |
| + p_pte = (PART_ENTRY_T *) p_mbr->partition + 0; |
| + p_fs->PBR_sector = GET32(p_pte->start_sector); |
| + p_fs->num_sectors = GET32(p_pte->num_sectors); |
| + |
| + if (p_fs->num_sectors == 0) { |
| + brelse(tmp_bh); |
| + bdev_close(sb); |
| + return FFS_ERROR; |
| + } |
| + |
| + if (sector_read(sb, p_fs->PBR_sector, &tmp_bh, 1) != FFS_SUCCESS) { |
| + bdev_close(sb); |
| + return FFS_MEDIAERR; |
| + } |
| + } else { |
| +#endif |
| + p_fs->PBR_sector = 0; |
| +#if (THERE_IS_MBR == 1) |
| + } |
| +#endif |
| + |
| + p_pbr = (PBR_SECTOR_T *) tmp_bh->b_data; |
| + |
| + if (GET16_A(p_pbr->signature) != PBR_SIGNATURE) { |
| + brelse(tmp_bh); |
| + bdev_close(sb); |
| + return FFS_FORMATERR; |
| + } |
| + |
| + for (i = 0; i < 53; i++) |
| + if (p_pbr->bpb[i]) |
| + break; |
| + |
| + if (i < 53) { |
| + if (GET16(p_pbr->bpb+11)) |
| + ret = fat16_mount(sb, p_pbr); |
| + else |
| + ret = fat32_mount(sb, p_pbr); |
| + } else { |
| + ret = exfat_mount(sb, p_pbr); |
| + } |
| + |
| + brelse(tmp_bh); |
| + |
| + if (ret) { |
| + bdev_close(sb); |
| + return ret; |
| + } |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + ret = load_alloc_bitmap(sb); |
| + if (ret) { |
| + bdev_close(sb); |
| + return ret; |
| + } |
| + ret = load_upcase_table(sb); |
| + if (ret) { |
| + free_alloc_bitmap(sb); |
| + bdev_close(sb); |
| + return ret; |
| + } |
| + } |
| + |
| + if (p_fs->dev_ejected) { |
| + if (p_fs->vol_type == EXFAT) { |
| + free_upcase_table(sb); |
| + free_alloc_bitmap(sb); |
| + } |
| + bdev_close(sb); |
| + return FFS_MEDIAERR; |
| + } |
| + |
| + printk("[EXFAT] mounted successfully\n"); |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsUmountVol(struct super_block *sb) |
| +{ |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + printk("[EXFAT] trying to unmount...\n"); |
| + |
| + fs_sync(sb, 0); |
| + fs_set_vol_flags(sb, VOL_CLEAN); |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + free_upcase_table(sb); |
| + free_alloc_bitmap(sb); |
| + } |
| + |
| + FAT_release_all(sb); |
| + buf_release_all(sb); |
| + |
| + bdev_close(sb); |
| + |
| + if (p_fs->dev_ejected) { |
| + printk( "[EXFAT] unmounted with media errors. " |
| + "device's already ejected.\n"); |
| + return FFS_MEDIAERR; |
| + } |
| + |
| + printk("[EXFAT] unmounted successfully\n"); |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info) |
| +{ |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (p_fs->used_clusters == (UINT32) ~0) |
| + p_fs->used_clusters = p_fs->fs_func->count_used_clusters(sb); |
| + |
| + info->FatType = p_fs->vol_type; |
| + info->ClusterSize = p_fs->cluster_size; |
| + info->NumClusters = p_fs->num_clusters - 2; |
| + info->UsedClusters = p_fs->used_clusters; |
| + info->FreeClusters = info->NumClusters - info->UsedClusters; |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsSyncVol(struct super_block *sb, INT32 do_sync) |
| +{ |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + fs_sync(sb, do_sync); |
| + fs_set_vol_flags(sb, VOL_CLEAN); |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid) |
| +{ |
| + INT32 ret, dentry, num_entries; |
| + CHAIN_T dir; |
| + UNI_NAME_T uni_name; |
| + DOS_NAME_T dos_name; |
| + DENTRY_T *ep, *ep2; |
| + ENTRY_SET_CACHE_T *es=NULL; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + PRINTK("ffsLookupFile entered\n"); |
| + |
| + ret = resolve_path(inode, path, &dir, &uni_name); |
| + if (ret) |
| + return ret; |
| + |
| + ret = get_num_entries_and_dos_name(sb, &dir, &uni_name, &num_entries, &dos_name); |
| + if (ret) |
| + return ret; |
| + |
| + dentry = p_fs->fs_func->find_dir_entry(sb, &dir, &uni_name, num_entries, &dos_name, TYPE_ALL); |
| + if (dentry < -1) |
| + return FFS_NOTFOUND; |
| + |
| + fid->dir.dir = dir.dir; |
| + fid->dir.size = dir.size; |
| + fid->dir.flags = dir.flags; |
| + fid->entry = dentry; |
| + |
| + if (dentry == -1) { |
| + fid->type = TYPE_DIR; |
| + fid->rwoffset = 0; |
| + fid->hint_last_off = -1; |
| + |
| + fid->attr = ATTR_SUBDIR; |
| + fid->flags = 0x01; |
| + fid->size = 0; |
| + fid->start_clu = p_fs->root_dir; |
| + } else { |
| + if (p_fs->vol_type == EXFAT) { |
| + es = get_entry_set_in_dir(sb, &dir, dentry, ES_2_ENTRIES, &ep); |
| + if (!es) |
| + return FFS_MEDIAERR; |
| + ep2 = ep+1; |
| + } else { |
| + ep = get_entry_in_dir(sb, &dir, dentry, NULL); |
| + if (!ep) |
| + return FFS_MEDIAERR; |
| + ep2 = ep; |
| + } |
| + |
| + fid->type = p_fs->fs_func->get_entry_type(ep); |
| + fid->rwoffset = 0; |
| + fid->hint_last_off = -1; |
| + fid->attr = p_fs->fs_func->get_entry_attr(ep); |
| + |
| + fid->size = p_fs->fs_func->get_entry_size(ep2); |
| + if ((fid->type == TYPE_FILE) && (fid->size == 0)) { |
| + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; |
| + fid->start_clu = CLUSTER_32(~0); |
| + } else { |
| + fid->flags = p_fs->fs_func->get_entry_flag(ep2); |
| + fid->start_clu = p_fs->fs_func->get_entry_clu0(ep2); |
| + } |
| + |
| + if (p_fs->vol_type == EXFAT) |
| + release_entry_set(es); |
| + } |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + PRINTK("ffsLookupFile exited successfully\n"); |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid) |
| +{ |
| + INT32 ret; |
| + CHAIN_T dir; |
| + UNI_NAME_T uni_name; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + ret = resolve_path(inode, path, &dir, &uni_name); |
| + if (ret) |
| + return ret; |
| + |
| + fs_set_vol_flags(sb, VOL_DIRTY); |
| + ret = create_file(inode, &dir, &uni_name, mode, fid); |
| + |
| +#if (DELAYED_SYNC == 0) |
| + fs_sync(sb, 0); |
| + fs_set_vol_flags(sb, VOL_CLEAN); |
| +#endif |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return ret; |
| +} |
| + |
| +INT32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount) |
| +{ |
| + INT32 offset, sec_offset, clu_offset; |
| + UINT32 clu, LogSector; |
| + UINT64 oneblkread, read_bytes; |
| + struct buffer_head *tmp_bh = NULL; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + |
| + if (fid->type != TYPE_FILE) |
| + return FFS_PERMISSIONERR; |
| + |
| + if (fid->rwoffset > fid->size) |
| + fid->rwoffset = fid->size; |
| + |
| + if (count > (fid->size - fid->rwoffset)) |
| + count = fid->size - fid->rwoffset; |
| + |
| + if (count == 0) { |
| + if (rcount != NULL) |
| + *rcount = 0; |
| + return FFS_EOF; |
| + } |
| + |
| + read_bytes = 0; |
| + |
| + while (count > 0) { |
| + clu_offset = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); |
| + clu = fid->start_clu; |
| + |
| + if (fid->flags == 0x03) { |
| + clu += clu_offset; |
| + } else { |
| + if ((clu_offset > 0) && (fid->hint_last_off > 0) && |
| + (clu_offset >= fid->hint_last_off)) { |
| + clu_offset -= fid->hint_last_off; |
| + clu = fid->hint_last_clu; |
| + } |
| + |
| + while (clu_offset > 0) { |
| + if (FAT_read(sb, clu, &clu) == -1) |
| + return FFS_MEDIAERR; |
| + |
| + clu_offset--; |
| + } |
| + } |
| + |
| + fid->hint_last_off = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); |
| + fid->hint_last_clu = clu; |
| + |
| + offset = (INT32)(fid->rwoffset & (p_fs->cluster_size-1)); |
| + sec_offset = offset >> p_bd->sector_size_bits; |
| + offset &= p_bd->sector_size_mask; |
| + |
| + LogSector = START_SECTOR(clu) + sec_offset; |
| + |
| + oneblkread = (UINT64)(p_bd->sector_size - offset); |
| + if (oneblkread > count) |
| + oneblkread = count; |
| + |
| + if ((offset == 0) && (oneblkread == p_bd->sector_size)) { |
| + if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) |
| + goto err_out; |
| + MEMCPY(((INT8 *) buffer)+read_bytes, ((INT8 *) tmp_bh->b_data), (INT32) oneblkread); |
| + } else { |
| + if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) |
| + goto err_out; |
| + MEMCPY(((INT8 *) buffer)+read_bytes, ((INT8 *) tmp_bh->b_data)+offset, (INT32) oneblkread); |
| + } |
| + count -= oneblkread; |
| + read_bytes += oneblkread; |
| + fid->rwoffset += oneblkread; |
| + } |
| + brelse(tmp_bh); |
| + |
| +err_out: |
| + if (rcount != NULL) |
| + *rcount = read_bytes; |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount) |
| +{ |
| + INT32 modified = FALSE, offset, sec_offset, clu_offset; |
| + INT32 num_clusters, num_alloc, num_alloced = (INT32) ~0; |
| + UINT32 clu, last_clu, LogSector, sector; |
| + UINT64 oneblkwrite, write_bytes; |
| + CHAIN_T new_clu; |
| + TIMESTAMP_T tm; |
| + DENTRY_T *ep, *ep2; |
| + ENTRY_SET_CACHE_T *es = NULL; |
| + struct buffer_head *tmp_bh = NULL; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + |
| + if (fid->type != TYPE_FILE) |
| + return FFS_PERMISSIONERR; |
| + |
| + if (fid->rwoffset > fid->size) |
| + fid->rwoffset = fid->size; |
| + |
| + if (count == 0) { |
| + if (wcount != NULL) |
| + *wcount = 0; |
| + return FFS_SUCCESS; |
| + } |
| + |
| + fs_set_vol_flags(sb, VOL_DIRTY); |
| + |
| + if (fid->size == 0) |
| + num_clusters = 0; |
| + else |
| + num_clusters = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; |
| + |
| + write_bytes = 0; |
| + |
| + while (count > 0) { |
| + clu_offset = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); |
| + clu = last_clu = fid->start_clu; |
| + |
| + if (fid->flags == 0x03) { |
| + if ((clu_offset > 0) && (clu != CLUSTER_32(~0))) { |
| + last_clu += clu_offset - 1; |
| + |
| + if (clu_offset == num_clusters) |
| + clu = CLUSTER_32(~0); |
| + else |
| + clu += clu_offset; |
| + } |
| + } else { |
| + if ((clu_offset > 0) && (fid->hint_last_off > 0) && |
| + (clu_offset >= fid->hint_last_off)) { |
| + clu_offset -= fid->hint_last_off; |
| + clu = fid->hint_last_clu; |
| + } |
| + |
| + while ((clu_offset > 0) && (clu != CLUSTER_32(~0))) { |
| + last_clu = clu; |
| + if (FAT_read(sb, clu, &clu) == -1) |
| + return FFS_MEDIAERR; |
| + |
| + clu_offset--; |
| + } |
| + } |
| + |
| + if (clu == CLUSTER_32(~0)) { |
| + num_alloc = (INT32)((count-1) >> p_fs->cluster_size_bits) + 1; |
| + new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) : last_clu+1; |
| + new_clu.size = 0; |
| + new_clu.flags = fid->flags; |
| + |
| + num_alloced = p_fs->fs_func->alloc_cluster(sb, num_alloc, &new_clu); |
| + if (num_alloced == 0) |
| + break; |
| + |
| + if (last_clu == CLUSTER_32(~0)) { |
| + if (new_clu.flags == 0x01) |
| + fid->flags = 0x01; |
| + fid->start_clu = new_clu.dir; |
| + modified = TRUE; |
| + } else { |
| + if (new_clu.flags != fid->flags) { |
| + exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters); |
| + fid->flags = 0x01; |
| + modified = TRUE; |
| + } |
| + if (new_clu.flags == 0x01) |
| + FAT_write(sb, last_clu, new_clu.dir); |
| + } |
| + |
| + num_clusters += num_alloced; |
| + clu = new_clu.dir; |
| + } |
| + |
| + fid->hint_last_off = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); |
| + fid->hint_last_clu = clu; |
| + |
| + offset = (INT32)(fid->rwoffset & (p_fs->cluster_size-1)); |
| + sec_offset = offset >> p_bd->sector_size_bits; |
| + offset &= p_bd->sector_size_mask; |
| + |
| + LogSector = START_SECTOR(clu) + sec_offset; |
| + |
| + oneblkwrite = (UINT64)(p_bd->sector_size - offset); |
| + if (oneblkwrite > count) |
| + oneblkwrite = count; |
| + |
| + if ((offset == 0) && (oneblkwrite == p_bd->sector_size)) { |
| + if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS) |
| + goto err_out; |
| + MEMCPY(((INT8 *) tmp_bh->b_data), ((INT8 *) buffer)+write_bytes, (INT32) oneblkwrite); |
| + if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) { |
| + brelse(tmp_bh); |
| + goto err_out; |
| + } |
| + } else { |
| + if ((offset > 0) || ((fid->rwoffset+oneblkwrite) < fid->size)) { |
| + if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) |
| + goto err_out; |
| + } else { |
| + if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS) |
| + goto err_out; |
| + } |
| + |
| + MEMCPY(((INT8 *) tmp_bh->b_data)+offset, ((INT8 *) buffer)+write_bytes, (INT32) oneblkwrite); |
| + if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) { |
| + brelse(tmp_bh); |
| + goto err_out; |
| + } |
| + } |
| + |
| + count -= oneblkwrite; |
| + write_bytes += oneblkwrite; |
| + fid->rwoffset += oneblkwrite; |
| + |
| + fid->attr |= ATTR_ARCHIVE; |
| + |
| + if (fid->size < fid->rwoffset) { |
| + fid->size = fid->rwoffset; |
| + modified = TRUE; |
| + } |
| + } |
| + |
| + brelse(tmp_bh); |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); |
| + if (es == NULL) |
| + goto err_out; |
| + ep2 = ep+1; |
| + } else { |
| + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); |
| + if (!ep) |
| + goto err_out; |
| + ep2 = ep; |
| + } |
| + |
| + p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY); |
| + p_fs->fs_func->set_entry_attr(ep, fid->attr); |
| + |
| + if (p_fs->vol_type != EXFAT) |
| + buf_modify(sb, sector); |
| + |
| + if (modified) { |
| + if (p_fs->fs_func->get_entry_flag(ep2) != fid->flags) |
| + p_fs->fs_func->set_entry_flag(ep2, fid->flags); |
| + |
| + if (p_fs->fs_func->get_entry_size(ep2) != fid->size) |
| + p_fs->fs_func->set_entry_size(ep2, fid->size); |
| + |
| + if (p_fs->fs_func->get_entry_clu0(ep2) != fid->start_clu) |
| + p_fs->fs_func->set_entry_clu0(ep2, fid->start_clu); |
| + |
| + if (p_fs->vol_type != EXFAT) |
| + buf_modify(sb, sector); |
| + } |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + update_dir_checksum_with_entry_set(sb, es); |
| + release_entry_set(es); |
| + } |
| + |
| +#if (DELAYED_SYNC == 0) |
| + fs_sync(sb, 0); |
| + fs_set_vol_flags(sb, VOL_CLEAN); |
| +#endif |
| + |
| +err_out: |
| + if (wcount != NULL) |
| + *wcount = write_bytes; |
| + |
| + if (num_alloced == 0) |
| + return FFS_FULL; |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size) |
| +{ |
| + INT32 num_clusters; |
| + UINT32 last_clu = CLUSTER_32(0), sector; |
| + CHAIN_T clu; |
| + TIMESTAMP_T tm; |
| + DENTRY_T *ep, *ep2; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); |
| + ENTRY_SET_CACHE_T *es=NULL; |
| + |
| + if (fid->type != TYPE_FILE) |
| + return FFS_PERMISSIONERR; |
| + |
| + if (fid->size != old_size) { |
| + printk(KERN_ERR "[EXFAT] truncate : can't skip it because of " |
| + "size-mismatch(old:%lld->fid:%lld).\n" |
| + ,old_size, fid->size); |
| + } |
| + |
| + if (old_size <= new_size) |
| + return FFS_SUCCESS; |
| + |
| + fs_set_vol_flags(sb, VOL_DIRTY); |
| + |
| + clu.dir = fid->start_clu; |
| + clu.size = (INT32)((old_size-1) >> p_fs->cluster_size_bits) + 1; |
| + clu.flags = fid->flags; |
| + |
| + if (new_size > 0) { |
| + num_clusters = (INT32)((new_size-1) >> p_fs->cluster_size_bits) + 1; |
| + |
| + if (clu.flags == 0x03) { |
| + clu.dir += num_clusters; |
| + } else { |
| + while (num_clusters > 0) { |
| + last_clu = clu.dir; |
| + if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) |
| + return FFS_MEDIAERR; |
| + num_clusters--; |
| + } |
| + } |
| + |
| + clu.size -= num_clusters; |
| + } |
| + |
| + fid->size = new_size; |
| + fid->attr |= ATTR_ARCHIVE; |
| + if (new_size == 0) { |
| + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; |
| + fid->start_clu = CLUSTER_32(~0); |
| + } |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); |
| + if (es == NULL) |
| + return FFS_MEDIAERR; |
| + ep2 = ep+1; |
| + } else { |
| + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); |
| + if (!ep) |
| + return FFS_MEDIAERR; |
| + ep2 = ep; |
| + } |
| + |
| + p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY); |
| + p_fs->fs_func->set_entry_attr(ep, fid->attr); |
| + |
| + p_fs->fs_func->set_entry_size(ep2, new_size); |
| + if (new_size == 0) { |
| + p_fs->fs_func->set_entry_flag(ep2, 0x01); |
| + p_fs->fs_func->set_entry_clu0(ep2, CLUSTER_32(0)); |
| + } |
| + |
| + if (p_fs->vol_type != EXFAT) |
| + buf_modify(sb, sector); |
| + else { |
| + update_dir_checksum_with_entry_set(sb, es); |
| + release_entry_set(es); |
| + } |
| + |
| + if (last_clu != CLUSTER_32(0)) { |
| + if (fid->flags == 0x01) |
| + FAT_write(sb, last_clu, CLUSTER_32(~0)); |
| + } |
| + |
| + p_fs->fs_func->free_cluster(sb, &clu, 0); |
| + |
| + fid->hint_last_off = -1; |
| + if (fid->rwoffset > fid->size) { |
| + fid->rwoffset = fid->size; |
| + } |
| + |
| +#if (DELAYED_SYNC == 0) |
| + fs_sync(sb, 0); |
| + fs_set_vol_flags(sb, VOL_CLEAN); |
| +#endif |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +static void update_parent_info( FILE_ID_T *fid, struct inode *parent_inode) |
| +{ |
| + FS_INFO_T *p_fs = &(EXFAT_SB(parent_inode->i_sb)->fs_info); |
| + FILE_ID_T *parent_fid = &(EXFAT_I(parent_inode)->fid); |
| + |
| + if (unlikely((parent_fid->flags != fid->dir.flags) |
| + || (parent_fid->size != (fid->dir.size<<p_fs->cluster_size_bits)) |
| + || (parent_fid->start_clu != fid->dir.dir))) { |
| + |
| + fid->dir.dir = parent_fid->start_clu; |
| + fid->dir.flags = parent_fid->flags; |
| + fid->dir.size = ((parent_fid->size + (p_fs->cluster_size-1)) |
| + >> p_fs->cluster_size_bits); |
| + } |
| +} |
| + |
| +INT32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) |
| +{ |
| + INT32 ret; |
| + INT32 dentry; |
| + CHAIN_T olddir, newdir; |
| + CHAIN_T *p_dir=NULL; |
| + UNI_NAME_T uni_name; |
| + DENTRY_T *ep; |
| + struct super_block *sb = old_parent_inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + UINT8 *new_path = (UINT8 *) new_dentry->d_name.name; |
| + struct inode *new_inode = new_dentry->d_inode; |
| + int num_entries; |
| + FILE_ID_T *new_fid = NULL; |
| + INT32 new_entry=0; |
| + |
| + if ((new_path == NULL) || (STRLEN(new_path) == 0)) |
| + return FFS_ERROR; |
| + |
| + update_parent_info(fid, old_parent_inode); |
| + |
| + olddir.dir = fid->dir.dir; |
| + olddir.size = fid->dir.size; |
| + olddir.flags = fid->dir.flags; |
| + |
| + dentry = fid->entry; |
| + |
| + if (p_fs->vol_type != EXFAT) { |
| + if ((olddir.dir != p_fs->root_dir) && (dentry < 2)) |
| + return FFS_PERMISSIONERR; |
| + } |
| + |
| + ep = get_entry_in_dir(sb, &olddir, dentry, NULL); |
| + if (!ep) |
| + return FFS_MEDIAERR; |
| + |
| + if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY) |
| + return FFS_PERMISSIONERR; |
| + |
| + if (new_inode) { |
| + UINT32 entry_type; |
| + |
| + ret = FFS_MEDIAERR; |
| + new_fid = &EXFAT_I(new_inode)->fid; |
| + |
| + update_parent_info(new_fid, new_parent_inode); |
| + |
| + p_dir = &(new_fid->dir); |
| + new_entry = new_fid->entry; |
| + ep = get_entry_in_dir(sb, p_dir, new_entry, NULL); |
| + if (!ep) |
| + goto out; |
| + |
| + entry_type = p_fs->fs_func->get_entry_type(ep); |
| + |
| + if (entry_type == TYPE_DIR) { |
| + CHAIN_T new_clu; |
| + new_clu.dir = new_fid->start_clu; |
| + new_clu.size = (INT32)((new_fid->size-1) >> p_fs->cluster_size_bits) + 1; |
| + new_clu.flags = new_fid->flags; |
| + |
| + if (!is_dir_empty(sb, &new_clu)) |
| + return FFS_FILEEXIST; |
| + } |
| + } |
| + |
| + ret = resolve_path(new_parent_inode, new_path, &newdir, &uni_name); |
| + if (ret) |
| + return ret; |
| + |
| + fs_set_vol_flags(sb, VOL_DIRTY); |
| + |
| + if (olddir.dir == newdir.dir) |
| + ret = rename_file(new_parent_inode, &olddir, dentry, &uni_name, fid); |
| + else |
| + ret = move_file(new_parent_inode, &olddir, dentry, &newdir, &uni_name, fid); |
| + |
| + if ((ret == FFS_SUCCESS) && new_inode) { |
| + ep = get_entry_in_dir(sb, p_dir, new_entry, NULL); |
| + if (!ep) |
| + goto out; |
| + |
| + num_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, new_entry, ep); |
| + if (num_entries < 0) |
| + goto out; |
| + p_fs->fs_func->delete_dir_entry(sb, p_dir, new_entry, 0, num_entries+1); |
| + } |
| +out: |
| +#if (DELAYED_SYNC == 0) |
| + fs_sync(sb, 0); |
| + fs_set_vol_flags(sb, VOL_CLEAN); |
| +#endif |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return ret; |
| +} |
| + |
| +INT32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid) |
| +{ |
| + INT32 dentry; |
| + CHAIN_T dir, clu_to_free; |
| + DENTRY_T *ep; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + dir.dir = fid->dir.dir; |
| + dir.size = fid->dir.size; |
| + dir.flags = fid->dir.flags; |
| + |
| + dentry = fid->entry; |
| + |
| + ep = get_entry_in_dir(sb, &dir, dentry, NULL); |
| + if (!ep) |
| + return FFS_MEDIAERR; |
| + |
| + if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY) |
| + return FFS_PERMISSIONERR; |
| + |
| + fs_set_vol_flags(sb, VOL_DIRTY); |
| + |
| + remove_file(inode, &dir, dentry); |
| + |
| + clu_to_free.dir = fid->start_clu; |
| + clu_to_free.size = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; |
| + clu_to_free.flags = fid->flags; |
| + |
| + p_fs->fs_func->free_cluster(sb, &clu_to_free, 0); |
| + |
| + |
| + fid->size = 0; |
| + fid->start_clu = CLUSTER_32(~0); |
| + fid->flags = (p_fs->vol_type == EXFAT)? 0x03: 0x01; |
| + |
| +#if (DELAYED_SYNC == 0) |
| + fs_sync(sb, 0); |
| + fs_set_vol_flags(sb, VOL_CLEAN); |
| +#endif |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsSetAttr(struct inode *inode, UINT32 attr) |
| +{ |
| + UINT32 type, sector; |
| + DENTRY_T *ep; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); |
| + UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; |
| + ENTRY_SET_CACHE_T *es = NULL; |
| + |
| + if (fid->attr == attr) { |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + return FFS_SUCCESS; |
| + } |
| + |
| + if (is_dir) { |
| + if ((fid->dir.dir == p_fs->root_dir) && |
| + (fid->entry == -1)) { |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + return FFS_SUCCESS; |
| + } |
| + } |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); |
| + if (es == NULL) |
| + return FFS_MEDIAERR; |
| + } else { |
| + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); |
| + if (!ep) |
| + return FFS_MEDIAERR; |
| + } |
| + |
| + type = p_fs->fs_func->get_entry_type(ep); |
| + |
| + if (((type == TYPE_FILE) && (attr & ATTR_SUBDIR)) || |
| + ((type == TYPE_DIR) && (!(attr & ATTR_SUBDIR)))) { |
| + INT32 err; |
| + if (p_fs->dev_ejected) |
| + err = FFS_MEDIAERR; |
| + else |
| + err = FFS_ERROR; |
| + |
| + if (p_fs->vol_type == EXFAT) |
| + release_entry_set(es); |
| + return err; |
| + } |
| + |
| + fs_set_vol_flags(sb, VOL_DIRTY); |
| + |
| + fid->attr = attr; |
| + p_fs->fs_func->set_entry_attr(ep, attr); |
| + |
| + if (p_fs->vol_type != EXFAT) |
| + buf_modify(sb, sector); |
| + else { |
| + update_dir_checksum_with_entry_set(sb, es); |
| + release_entry_set(es); |
| + } |
| + |
| +#if (DELAYED_SYNC == 0) |
| + fs_sync(sb, 0); |
| + fs_set_vol_flags(sb, VOL_CLEAN); |
| +#endif |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info) |
| +{ |
| + UINT32 sector; |
| + INT32 count; |
| + CHAIN_T dir; |
| + UNI_NAME_T uni_name; |
| + TIMESTAMP_T tm; |
| + DENTRY_T *ep, *ep2; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); |
| + ENTRY_SET_CACHE_T *es=NULL; |
| + UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; |
| + |
| + PRINTK("ffsGetStat entered\n"); |
| + |
| + if (is_dir) { |
| + if ((fid->dir.dir == p_fs->root_dir) && |
| + (fid->entry == -1)) { |
| + info->Attr = ATTR_SUBDIR; |
| + MEMSET((INT8 *) &info->CreateTimestamp, 0, sizeof(DATE_TIME_T)); |
| + MEMSET((INT8 *) &info->ModifyTimestamp, 0, sizeof(DATE_TIME_T)); |
| + MEMSET((INT8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); |
| + STRCPY(info->ShortName, "."); |
| + STRCPY(info->Name, "."); |
| + |
| + dir.dir = p_fs->root_dir; |
| + dir.flags = 0x01; |
| + |
| + if (p_fs->root_dir == CLUSTER_32(0)) |
| + info->Size = p_fs->dentries_in_root << DENTRY_SIZE_BITS; |
| + else |
| + info->Size = count_num_clusters(sb, &dir) << p_fs->cluster_size_bits; |
| + |
| + count = count_dos_name_entries(sb, &dir, TYPE_DIR); |
| + if (count < 0) |
| + return FFS_MEDIAERR; |
| + info->NumSubdirs = count; |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + return FFS_SUCCESS; |
| + } |
| + } |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_2_ENTRIES, &ep); |
| + if (es == NULL) |
| + return FFS_MEDIAERR; |
| + ep2 = ep+1; |
| + } else { |
| + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); |
| + if (!ep) |
| + return FFS_MEDIAERR; |
| + ep2 = ep; |
| + buf_lock(sb, sector); |
| + } |
| + |
| + info->Attr = p_fs->fs_func->get_entry_attr(ep); |
| + |
| + p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE); |
| + info->CreateTimestamp.Year = tm.year; |
| + info->CreateTimestamp.Month = tm.mon; |
| + info->CreateTimestamp.Day = tm.day; |
| + info->CreateTimestamp.Hour = tm.hour; |
| + info->CreateTimestamp.Minute = tm.min; |
| + info->CreateTimestamp.Second = tm.sec; |
| + info->CreateTimestamp.MilliSecond = 0; |
| + |
| + p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY); |
| + info->ModifyTimestamp.Year = tm.year; |
| + info->ModifyTimestamp.Month = tm.mon; |
| + info->ModifyTimestamp.Day = tm.day; |
| + info->ModifyTimestamp.Hour = tm.hour; |
| + info->ModifyTimestamp.Minute = tm.min; |
| + info->ModifyTimestamp.Second = tm.sec; |
| + info->ModifyTimestamp.MilliSecond = 0; |
| + |
| + MEMSET((INT8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); |
| + |
| + *(uni_name.name) = 0x0; |
| + p_fs->fs_func->get_uni_name_from_ext_entry(sb, &(fid->dir), fid->entry, uni_name.name); |
| + if (*(uni_name.name) == 0x0) |
| + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); |
| + nls_uniname_to_cstring(sb, info->Name, &uni_name); |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + info->NumSubdirs = 2; |
| + } else { |
| + buf_unlock(sb, sector); |
| + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0); |
| + nls_uniname_to_cstring(sb, info->ShortName, &uni_name); |
| + info->NumSubdirs = 0; |
| + } |
| + |
| + info->Size = p_fs->fs_func->get_entry_size(ep2); |
| + |
| + if (p_fs->vol_type == EXFAT) |
| + release_entry_set(es); |
| + |
| + if (is_dir) { |
| + dir.dir = fid->start_clu; |
| + dir.flags = 0x01; |
| + |
| + if (info->Size == 0) |
| + info->Size = (UINT64) count_num_clusters(sb, &dir) << p_fs->cluster_size_bits; |
| + |
| + count = count_dos_name_entries(sb, &dir, TYPE_DIR); |
| + if (count < 0) |
| + return FFS_MEDIAERR; |
| + info->NumSubdirs += count; |
| + } |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + PRINTK("ffsGetStat exited successfully\n"); |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info) |
| +{ |
| + UINT32 sector; |
| + TIMESTAMP_T tm; |
| + DENTRY_T *ep, *ep2; |
| + ENTRY_SET_CACHE_T *es=NULL; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); |
| + UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; |
| + |
| + if (is_dir) { |
| + if ((fid->dir.dir == p_fs->root_dir) && |
| + (fid->entry == -1)) { |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + return FFS_SUCCESS; |
| + } |
| + } |
| + |
| + fs_set_vol_flags(sb, VOL_DIRTY); |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); |
| + if (es == NULL) |
| + return FFS_MEDIAERR; |
| + ep2 = ep+1; |
| + } else { |
| + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); |
| + if (!ep) |
| + return FFS_MEDIAERR; |
| + ep2 = ep; |
| + } |
| + |
| + |
| + p_fs->fs_func->set_entry_attr(ep, info->Attr); |
| + |
| + tm.sec = info->CreateTimestamp.Second; |
| + tm.min = info->CreateTimestamp.Minute; |
| + tm.hour = info->CreateTimestamp.Hour; |
| + tm.day = info->CreateTimestamp.Day; |
| + tm.mon = info->CreateTimestamp.Month; |
| + tm.year = info->CreateTimestamp.Year; |
| + p_fs->fs_func->set_entry_time(ep, &tm, TM_CREATE); |
| + |
| + tm.sec = info->ModifyTimestamp.Second; |
| + tm.min = info->ModifyTimestamp.Minute; |
| + tm.hour = info->ModifyTimestamp.Hour; |
| + tm.day = info->ModifyTimestamp.Day; |
| + tm.mon = info->ModifyTimestamp.Month; |
| + tm.year = info->ModifyTimestamp.Year; |
| + p_fs->fs_func->set_entry_time(ep, &tm, TM_MODIFY); |
| + |
| + |
| + p_fs->fs_func->set_entry_size(ep2, info->Size); |
| + |
| + if (p_fs->vol_type != EXFAT) { |
| + buf_modify(sb, sector); |
| + } else { |
| + update_dir_checksum_with_entry_set(sb, es); |
| + release_entry_set(es); |
| + } |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu) |
| +{ |
| + INT32 num_clusters, num_alloced, modified = FALSE; |
| + UINT32 last_clu, sector; |
| + CHAIN_T new_clu; |
| + DENTRY_T *ep; |
| + ENTRY_SET_CACHE_T *es = NULL; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); |
| + |
| + fid->rwoffset = (INT64)(clu_offset) << p_fs->cluster_size_bits; |
| + |
| + if (EXFAT_I(inode)->mmu_private == 0) |
| + num_clusters = 0; |
| + else |
| + num_clusters = (INT32)((EXFAT_I(inode)->mmu_private-1) >> p_fs->cluster_size_bits) + 1; |
| + |
| + *clu = last_clu = fid->start_clu; |
| + |
| + if (fid->flags == 0x03) { |
| + if ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) { |
| + last_clu += clu_offset - 1; |
| + |
| + if (clu_offset == num_clusters) |
| + *clu = CLUSTER_32(~0); |
| + else |
| + *clu += clu_offset; |
| + } |
| + } else { |
| + if ((clu_offset > 0) && (fid->hint_last_off > 0) && |
| + (clu_offset >= fid->hint_last_off)) { |
| + clu_offset -= fid->hint_last_off; |
| + *clu = fid->hint_last_clu; |
| + } |
| + |
| + while ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) { |
| + last_clu = *clu; |
| + if (FAT_read(sb, *clu, clu) == -1) |
| + return FFS_MEDIAERR; |
| + clu_offset--; |
| + } |
| + } |
| + |
| + if (*clu == CLUSTER_32(~0)) { |
| + fs_set_vol_flags(sb, VOL_DIRTY); |
| + |
| + new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) : last_clu+1; |
| + new_clu.size = 0; |
| + new_clu.flags = fid->flags; |
| + |
| + num_alloced = p_fs->fs_func->alloc_cluster(sb, 1, &new_clu); |
| + if (num_alloced < 1) |
| + return FFS_FULL; |
| + |
| + if (last_clu == CLUSTER_32(~0)) { |
| + if (new_clu.flags == 0x01) |
| + fid->flags = 0x01; |
| + fid->start_clu = new_clu.dir; |
| + modified = TRUE; |
| + } else { |
| + if (new_clu.flags != fid->flags) { |
| + exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters); |
| + fid->flags = 0x01; |
| + modified = TRUE; |
| + } |
| + if (new_clu.flags == 0x01) |
| + FAT_write(sb, last_clu, new_clu.dir); |
| + } |
| + |
| + num_clusters += num_alloced; |
| + *clu = new_clu.dir; |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); |
| + if (es == NULL) |
| + return FFS_MEDIAERR; |
| + ep++; |
| + } |
| + |
| + if (modified) { |
| + if (p_fs->vol_type != EXFAT) { |
| + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); |
| + if (!ep) |
| + return FFS_MEDIAERR; |
| + } |
| + |
| + if (p_fs->fs_func->get_entry_flag(ep) != fid->flags) |
| + p_fs->fs_func->set_entry_flag(ep, fid->flags); |
| + |
| + if (p_fs->fs_func->get_entry_clu0(ep) != fid->start_clu) |
| + p_fs->fs_func->set_entry_clu0(ep, fid->start_clu); |
| + |
| + if (p_fs->vol_type != EXFAT) |
| + buf_modify(sb, sector); |
| + } |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + update_dir_checksum_with_entry_set(sb, es); |
| + release_entry_set(es); |
| + } |
| + |
| + inode->i_blocks += num_alloced << (p_fs->cluster_size_bits - 9); |
| + } |
| + |
| + fid->hint_last_off = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); |
| + fid->hint_last_clu = *clu; |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid) |
| +{ |
| + INT32 ret; |
| + CHAIN_T dir; |
| + UNI_NAME_T uni_name; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + PRINTK("ffsCreateDir entered\n"); |
| + |
| + ret = resolve_path(inode, path, &dir, &uni_name); |
| + if (ret) |
| + return ret; |
| + |
| + fs_set_vol_flags(sb, VOL_DIRTY); |
| + |
| + ret = create_dir(inode, &dir, &uni_name, fid); |
| + |
| +#if (DELAYED_SYNC == 0) |
| + fs_sync(sb, 0); |
| + fs_set_vol_flags(sb, VOL_CLEAN); |
| +#endif |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return ret; |
| +} |
| + |
| +INT32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) |
| +{ |
| + INT32 i, dentry, clu_offset; |
| + INT32 dentries_per_clu, dentries_per_clu_bits = 0; |
| + UINT32 type, sector; |
| + CHAIN_T dir, clu; |
| + UNI_NAME_T uni_name; |
| + TIMESTAMP_T tm; |
| + DENTRY_T *ep; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); |
| + |
| + if (fid->type != TYPE_DIR) |
| + return FFS_PERMISSIONERR; |
| + |
| + if (fid->entry == -1) { |
| + dir.dir = p_fs->root_dir; |
| + dir.flags = 0x01; |
| + } else { |
| + dir.dir = fid->start_clu; |
| + dir.size = (INT32)(fid->size >> p_fs->cluster_size_bits); |
| + dir.flags = fid->flags; |
| + } |
| + |
| + dentry = (INT32) fid->rwoffset; |
| + |
| + if (dir.dir == CLUSTER_32(0)) { |
| + dentries_per_clu = p_fs->dentries_in_root; |
| + |
| + if (dentry == dentries_per_clu) { |
| + clu.dir = CLUSTER_32(~0); |
| + } else { |
| + clu.dir = dir.dir; |
| + clu.size = dir.size; |
| + clu.flags = dir.flags; |
| + } |
| + } else { |
| + dentries_per_clu = p_fs->dentries_per_clu; |
| + dentries_per_clu_bits = my_log2(dentries_per_clu); |
| + |
| + clu_offset = dentry >> dentries_per_clu_bits; |
| + clu.dir = dir.dir; |
| + clu.size = dir.size; |
| + clu.flags = dir.flags; |
| + |
| + if (clu.flags == 0x03) { |
| + clu.dir += clu_offset; |
| + clu.size -= clu_offset; |
| + } else { |
| + if ((clu_offset > 0) && (fid->hint_last_off > 0) && |
| + (clu_offset >= fid->hint_last_off)) { |
| + clu_offset -= fid->hint_last_off; |
| + clu.dir = fid->hint_last_clu; |
| + } |
| + |
| + while (clu_offset > 0) { |
| + if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) |
| + return FFS_MEDIAERR; |
| + |
| + clu_offset--; |
| + } |
| + } |
| + } |
| + |
| + while (clu.dir != CLUSTER_32(~0)) { |
| + if (p_fs->dev_ejected) |
| + break; |
| + |
| + if (dir.dir == CLUSTER_32(0)) |
| + 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, §or); |
| + if (!ep) |
| + return FFS_MEDIAERR; |
| + |
| + type = p_fs->fs_func->get_entry_type(ep); |
| + |
| + if (type == TYPE_UNUSED) |
| + break; |
| + |
| + if ((type != TYPE_FILE) && (type != TYPE_DIR)) |
| + continue; |
| + |
| + buf_lock(sb, sector); |
| + dir_entry->Attr = p_fs->fs_func->get_entry_attr(ep); |
| + |
| + p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE); |
| + dir_entry->CreateTimestamp.Year = tm.year; |
| + dir_entry->CreateTimestamp.Month = tm.mon; |
| + dir_entry->CreateTimestamp.Day = tm.day; |
| + dir_entry->CreateTimestamp.Hour = tm.hour; |
| + dir_entry->CreateTimestamp.Minute = tm.min; |
| + dir_entry->CreateTimestamp.Second = tm.sec; |
| + dir_entry->CreateTimestamp.MilliSecond = 0; |
| + |
| + p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY); |
| + dir_entry->ModifyTimestamp.Year = tm.year; |
| + dir_entry->ModifyTimestamp.Month = tm.mon; |
| + dir_entry->ModifyTimestamp.Day = tm.day; |
| + dir_entry->ModifyTimestamp.Hour = tm.hour; |
| + dir_entry->ModifyTimestamp.Minute = tm.min; |
| + dir_entry->ModifyTimestamp.Second = tm.sec; |
| + dir_entry->ModifyTimestamp.MilliSecond = 0; |
| + |
| + MEMSET((INT8 *) &dir_entry->AccessTimestamp, 0, sizeof(DATE_TIME_T)); |
| + |
| + *(uni_name.name) = 0x0; |
| + p_fs->fs_func->get_uni_name_from_ext_entry(sb, &dir, dentry, uni_name.name); |
| + if (*(uni_name.name) == 0x0) |
| + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); |
| + nls_uniname_to_cstring(sb, dir_entry->Name, &uni_name); |
| + buf_unlock(sb, sector); |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + ep = get_entry_in_dir(sb, &clu, i+1, NULL); |
| + if (!ep) |
| + return FFS_MEDIAERR; |
| + } else { |
| + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0); |
| + nls_uniname_to_cstring(sb, dir_entry->ShortName, &uni_name); |
| + } |
| + |
| + dir_entry->Size = p_fs->fs_func->get_entry_size(ep); |
| + |
| + if (dir.dir == CLUSTER_32(0)) { |
| + } else { |
| + fid->hint_last_off = dentry >> dentries_per_clu_bits; |
| + fid->hint_last_clu = clu.dir; |
| + } |
| + |
| + fid->rwoffset = (INT64) ++dentry; |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return FFS_SUCCESS; |
| + } |
| + |
| + if (dir.dir == CLUSTER_32(0)) |
| + break; |
| + |
| + if (clu.flags == 0x03) { |
| + if ((--clu.size) > 0) |
| + clu.dir++; |
| + else |
| + clu.dir = CLUSTER_32(~0); |
| + } else { |
| + if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) |
| + return FFS_MEDIAERR; |
| + } |
| + } |
| + |
| + *(dir_entry->Name) = '\0'; |
| + |
| + fid->rwoffset = (INT64) ++dentry; |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid) |
| +{ |
| + INT32 dentry; |
| + CHAIN_T dir, clu_to_free; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + dir.dir = fid->dir.dir; |
| + dir.size = fid->dir.size; |
| + dir.flags = fid->dir.flags; |
| + |
| + dentry = fid->entry; |
| + |
| + if (p_fs->vol_type != EXFAT) { |
| + if ((dir.dir != p_fs->root_dir) && (dentry < 2)) |
| + return FFS_PERMISSIONERR; |
| + } |
| + |
| + clu_to_free.dir = fid->start_clu; |
| + clu_to_free.size = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; |
| + clu_to_free.flags = fid->flags; |
| + |
| + if (!is_dir_empty(sb, &clu_to_free)) |
| + return FFS_FILEEXIST; |
| + |
| + fs_set_vol_flags(sb, VOL_DIRTY); |
| + |
| + remove_file(inode, &dir, dentry); |
| + |
| + p_fs->fs_func->free_cluster(sb, &clu_to_free, 1); |
| + |
| + fid->size = 0; |
| + fid->start_clu = CLUSTER_32(~0); |
| + fid->flags = (p_fs->vol_type == EXFAT)? 0x03: 0x01; |
| + |
| +#if (DELAYED_SYNC == 0) |
| + fs_sync(sb, 0); |
| + fs_set_vol_flags(sb, VOL_CLEAN); |
| +#endif |
| + |
| + if (p_fs->dev_ejected) |
| + return FFS_MEDIAERR; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 fs_init(void) |
| +{ |
| + if (sizeof(DENTRY_T) != DENTRY_SIZE) { |
| + return FFS_ALIGNMENTERR; |
| + } |
| + |
| + if (sizeof(DOS_DENTRY_T) != DENTRY_SIZE) { |
| + return FFS_ALIGNMENTERR; |
| + } |
| + |
| + if (sizeof(EXT_DENTRY_T) != DENTRY_SIZE) { |
| + return FFS_ALIGNMENTERR; |
| + } |
| + |
| + if (sizeof(FILE_DENTRY_T) != DENTRY_SIZE) { |
| + return FFS_ALIGNMENTERR; |
| + } |
| + |
| + if (sizeof(STRM_DENTRY_T) != DENTRY_SIZE) { |
| + return FFS_ALIGNMENTERR; |
| + } |
| + |
| + if (sizeof(NAME_DENTRY_T) != DENTRY_SIZE) { |
| + return FFS_ALIGNMENTERR; |
| + } |
| + |
| + if (sizeof(BMAP_DENTRY_T) != DENTRY_SIZE) { |
| + return FFS_ALIGNMENTERR; |
| + } |
| + |
| + if (sizeof(CASE_DENTRY_T) != DENTRY_SIZE) { |
| + return FFS_ALIGNMENTERR; |
| + } |
| + |
| + if (sizeof(VOLM_DENTRY_T) != DENTRY_SIZE) { |
| + return FFS_ALIGNMENTERR; |
| + } |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 fs_shutdown(void) |
| +{ |
| + return FFS_SUCCESS; |
| +} |
| + |
| +void fs_set_vol_flags(struct super_block *sb, UINT32 new_flag) |
| +{ |
| + PBR_SECTOR_T *p_pbr; |
| + BPBEX_T *p_bpb; |
| + 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->vol_type == EXFAT) { |
| + if (p_fs->pbr_bh == NULL) { |
| + if (sector_read(sb, p_fs->PBR_sector, &(p_fs->pbr_bh), 1) != FFS_SUCCESS) |
| + return; |
| + } |
| + |
| + p_pbr = (PBR_SECTOR_T *) p_fs->pbr_bh->b_data; |
| + p_bpb = (BPBEX_T *) p_pbr->bpb; |
| + SET16(p_bpb->vol_flags, (UINT16) new_flag); |
| + |
| + 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_sync(struct super_block *sb, INT32 do_sync) |
| +{ |
| + if (do_sync) |
| + bdev_sync(sb); |
| +} |
| + |
| +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->s_flags & MS_RDONLY)) { |
| + sb->s_flags |= MS_RDONLY; |
| + printk(KERN_ERR "[EXFAT] Filesystem has been set read-only\n"); |
| + } |
| +} |
| + |
| +INT32 clear_cluster(struct super_block *sb, UINT32 clu) |
| +{ |
| + UINT32 s, n; |
| + INT32 ret = FFS_SUCCESS; |
| + struct buffer_head *tmp_bh = NULL; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + |
| + if (clu == CLUSTER_32(0)) { |
| + 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++) { |
| + if ((ret = sector_read(sb, s, &tmp_bh, 0)) != FFS_SUCCESS) |
| + return ret; |
| + |
| + MEMSET((INT8 *) tmp_bh->b_data, 0x0, p_bd->sector_size); |
| + if ((ret = sector_write(sb, s, tmp_bh, 0)) !=FFS_SUCCESS) |
| + break; |
| + } |
| + |
| + brelse(tmp_bh); |
| + return ret; |
| +} |
| + |
| +INT32 fat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain) |
| +{ |
| + INT32 i, num_clusters = 0; |
| + UINT32 new_clu, last_clu = CLUSTER_32(~0), read_clu; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + new_clu = p_chain->dir; |
| + if (new_clu == CLUSTER_32(~0)) |
| + new_clu = p_fs->clu_srch_ptr; |
| + else if (new_clu >= p_fs->num_clusters) |
| + new_clu = 2; |
| + |
| + __set_sb_dirty(sb); |
| + |
| + p_chain->dir = CLUSTER_32(~0); |
| + |
| + for (i = 2; i < p_fs->num_clusters; i++) { |
| + if (FAT_read(sb, new_clu, &read_clu) != 0) |
| + return 0; |
| + |
| + if (read_clu == CLUSTER_32(0)) { |
| + FAT_write(sb, new_clu, CLUSTER_32(~0)); |
| + num_clusters++; |
| + |
| + if (p_chain->dir == CLUSTER_32(~0)) |
| + p_chain->dir = new_clu; |
| + else |
| + FAT_write(sb, last_clu, new_clu); |
| + |
| + last_clu = new_clu; |
| + |
| + if ((--num_alloc) == 0) { |
| + p_fs->clu_srch_ptr = new_clu; |
| + if (p_fs->used_clusters != (UINT32) ~0) |
| + p_fs->used_clusters += num_clusters; |
| + |
| + return(num_clusters); |
| + } |
| + } |
| + if ((++new_clu) >= p_fs->num_clusters) |
| + new_clu = 2; |
| + } |
| + |
| + p_fs->clu_srch_ptr = new_clu; |
| + if (p_fs->used_clusters != (UINT32) ~0) |
| + p_fs->used_clusters += num_clusters; |
| + |
| + return(num_clusters); |
| +} |
| + |
| +INT32 exfat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain) |
| +{ |
| + INT32 num_clusters = 0; |
| + UINT32 hint_clu, new_clu, last_clu = CLUSTER_32(~0); |
| + 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) != FFS_SUCCESS) |
| + return 0; |
| + |
| + num_clusters++; |
| + |
| + if (p_chain->flags == 0x01) |
| + FAT_write(sb, new_clu, CLUSTER_32(~0)); |
| + |
| + if (p_chain->dir == CLUSTER_32(~0)) { |
| + p_chain->dir = new_clu; |
| + } else { |
| + if (p_chain->flags == 0x01) |
| + FAT_write(sb, last_clu, new_clu); |
| + } |
| + last_clu = new_clu; |
| + |
| + if ((--num_alloc) == 0) { |
| + p_fs->clu_srch_ptr = hint_clu; |
| + if (p_fs->used_clusters != (UINT32) ~0) |
| + 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 != (UINT32) ~0) |
| + p_fs->used_clusters += num_clusters; |
| + |
| + p_chain->size += num_clusters; |
| + return(num_clusters); |
| +} |
| + |
| +void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse) |
| +{ |
| + INT32 num_clusters = 0; |
| + UINT32 clu, prev; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + INT32 i; |
| + UINT32 sector; |
| + |
| + if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) |
| + return; |
| + __set_sb_dirty(sb); |
| + clu = p_chain->dir; |
| + |
| + if (p_chain->size <= 0) |
| + return; |
| + |
| + do { |
| + if (p_fs->dev_ejected) |
| + break; |
| + |
| + if (do_relse) { |
| + sector = START_SECTOR(clu); |
| + for (i = 0; i < p_fs->sectors_per_clu; i++) { |
| + buf_release(sb, sector+i); |
| + } |
| + } |
| + |
| + prev = clu; |
| + if (FAT_read(sb, clu, &clu) == -1) |
| + break; |
| + |
| + FAT_write(sb, prev, CLUSTER_32(0)); |
| + num_clusters++; |
| + |
| + } while (clu != CLUSTER_32(~0)); |
| + |
| + if (p_fs->used_clusters != (UINT32) ~0) |
| + p_fs->used_clusters -= num_clusters; |
| +} |
| + |
| +void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse) |
| +{ |
| + INT32 num_clusters = 0; |
| + UINT32 clu; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + INT32 i; |
| + UINT32 sector; |
| + |
| + if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) |
| + return; |
| + |
| + if (p_chain->size <= 0) { |
| + printk(KERN_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++) { |
| + buf_release(sb, sector+i); |
| + } |
| + } |
| + |
| + if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS) |
| + 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++) { |
| + buf_release(sb, sector+i); |
| + } |
| + } |
| + |
| + if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS) |
| + break; |
| + |
| + if (FAT_read(sb, clu, &clu) == -1) |
| + break; |
| + num_clusters++; |
| + } while ((clu != CLUSTER_32(0)) && (clu != CLUSTER_32(~0))); |
| + } |
| + |
| + if (p_fs->used_clusters != (UINT32) ~0) |
| + p_fs->used_clusters -= num_clusters; |
| +} |
| + |
| +UINT32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain) |
| +{ |
| + UINT32 clu, next; |
| + 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((FAT_read(sb, clu, &next) == 0) && (next != CLUSTER_32(~0))) { |
| + if (p_fs->dev_ejected) |
| + break; |
| + clu = next; |
| + } |
| + } |
| + |
| + return(clu); |
| +} |
| + |
| +INT32 count_num_clusters(struct super_block *sb, CHAIN_T *p_chain) |
| +{ |
| + INT32 i, count = 0; |
| + UINT32 clu; |
| + 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 (FAT_read(sb, clu, &clu) != 0) |
| + return 0; |
| + if (clu == CLUSTER_32(~0)) |
| + break; |
| + } |
| + } |
| + |
| + return(count); |
| +} |
| + |
| +INT32 fat_count_used_clusters(struct super_block *sb) |
| +{ |
| + INT32 i, count = 0; |
| + UINT32 clu; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + for (i = 2; i < p_fs->num_clusters; i++) { |
| + if (FAT_read(sb, i, &clu) != 0) |
| + break; |
| + if (clu != CLUSTER_32(0)) |
| + count++; |
| + } |
| + |
| + return(count); |
| +} |
| + |
| +INT32 exfat_count_used_clusters(struct super_block *sb) |
| +{ |
| + INT32 i, map_i, map_b, count = 0; |
| + UINT8 k; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + |
| + map_i = map_b = 0; |
| + |
| + for (i = 2; i < p_fs->num_clusters; i += 8) { |
| + k = *(((UINT8 *) 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, UINT32 chain, INT32 len) |
| +{ |
| + if (len == 0) |
| + return; |
| + |
| + while (len > 1) { |
| + FAT_write(sb, chain, chain+1); |
| + chain++; |
| + len--; |
| + } |
| + FAT_write(sb, chain, CLUSTER_32(~0)); |
| +} |
| + |
| +INT32 load_alloc_bitmap(struct super_block *sb) |
| +{ |
| + INT32 i, j, ret; |
| + UINT32 map_size; |
| + UINT32 type, sector; |
| + CHAIN_T clu; |
| + BMAP_DENTRY_T *ep; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + 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 = (BMAP_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); |
| + if (!ep) |
| + return FFS_MEDIAERR; |
| + |
| + type = p_fs->fs_func->get_entry_type((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 = (UINT32) GET64_A(ep->size); |
| + |
| + p_fs->map_sectors = ((map_size-1) >> p_bd->sector_size_bits) + 1; |
| + |
| + p_fs->vol_amap = (struct buffer_head **) MALLOC(sizeof(struct buffer_head *) * p_fs->map_sectors); |
| + if (p_fs->vol_amap == NULL) |
| + return FFS_MEMORYERR; |
| + |
| + 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 != FFS_SUCCESS) { |
| + i=0; |
| + while (i < j) |
| + brelse(p_fs->vol_amap[i++]); |
| + |
| + FREE(p_fs->vol_amap); |
| + p_fs->vol_amap = NULL; |
| + return ret; |
| + } |
| + } |
| + |
| + p_fs->pbr_bh = NULL; |
| + return FFS_SUCCESS; |
| + } |
| + } |
| + |
| + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) |
| + return FFS_MEDIAERR; |
| + } |
| + |
| + return FFS_FORMATERR; |
| +} |
| + |
| +void free_alloc_bitmap(struct super_block *sb) |
| +{ |
| + INT32 i; |
| + 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]); |
| + } |
| + |
| + FREE(p_fs->vol_amap); |
| + p_fs->vol_amap = NULL; |
| +} |
| + |
| +INT32 set_alloc_bitmap(struct super_block *sb, UINT32 clu) |
| +{ |
| + INT32 i, b; |
| + UINT32 sector; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + 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; |
| + |
| + Bitmap_set((UINT8 *) p_fs->vol_amap[i]->b_data, b); |
| + |
| + return (sector_write(sb, sector, p_fs->vol_amap[i], 0)); |
| +} |
| + |
| +INT32 clr_alloc_bitmap(struct super_block *sb, UINT32 clu) |
| +{ |
| + INT32 i, b; |
| + UINT32 sector; |
| +#if EXFAT_CONFIG_DISCARD |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + struct exfat_mount_options *opts = &sbi->options; |
| + int ret; |
| +#endif |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + 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; |
| + |
| + Bitmap_clear((UINT8 *) p_fs->vol_amap[i]->b_data, b); |
| + |
| + return (sector_write(sb, sector, p_fs->vol_amap[i], 0)); |
| + |
| +#if EXFAT_CONFIG_DISCARD |
| + if (opts->discard) { |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) |
| + ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits)); |
| +#else |
| + ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits), GFP_NOFS, 0); |
| +#endif |
| + if (ret == -EOPNOTSUPP) { |
| + printk(KERN_WARNING "discard not supported by device, disabling"); |
| + opts->discard = 0; |
| + } |
| + } |
| +#endif |
| +} |
| + |
| +UINT32 test_alloc_bitmap(struct super_block *sb, UINT32 clu) |
| +{ |
| + INT32 i, map_i, map_b; |
| + UINT32 clu_base, clu_free; |
| + UINT8 k, clu_mask; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + 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 = *(((UINT8 *) 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)); |
| +} |
| + |
| +void sync_alloc_bitmap(struct super_block *sb) |
| +{ |
| + INT32 i; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (p_fs->vol_amap == NULL) |
| + return; |
| + |
| + for (i = 0; i < p_fs->map_sectors; i++) { |
| + sync_dirty_buffer(p_fs->vol_amap[i]); |
| + } |
| +} |
| + |
| +INT32 __load_upcase_table(struct super_block *sb, UINT32 sector, UINT32 num_sectors, UINT32 utbl_checksum) |
| +{ |
| + INT32 i, ret = FFS_ERROR; |
| + UINT32 j; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + struct buffer_head *tmp_bh = NULL; |
| + |
| + UINT8 skip = FALSE; |
| + UINT32 index = 0; |
| + UINT16 uni = 0; |
| + UINT16 **upcase_table; |
| + |
| + UINT32 checksum = 0; |
| + |
| + upcase_table = p_fs->vol_utbl = (UINT16 **) MALLOC(UTBL_COL_COUNT * sizeof(UINT16 *)); |
| + if(upcase_table == NULL) |
| + return FFS_MEMORYERR; |
| + MEMSET(upcase_table, 0, UTBL_COL_COUNT * sizeof(UINT16 *)); |
| + |
| + num_sectors += sector; |
| + |
| + while(sector < num_sectors) { |
| + ret = sector_read(sb, sector, &tmp_bh, 1); |
| + if (ret != FFS_SUCCESS) { |
| + PRINTK("sector read (0x%X)fail\n", sector); |
| + goto error; |
| + } |
| + sector++; |
| + |
| + for(i = 0; i < p_bd->sector_size && index <= 0xFFFF; i += 2) { |
| + uni = GET16(((UINT8 *) tmp_bh->b_data)+i); |
| + |
| + checksum = ((checksum & 1) ? 0x80000000 : 0 ) + (checksum >> 1) + *(((UINT8 *) tmp_bh->b_data)+i); |
| + checksum = ((checksum & 1) ? 0x80000000 : 0 ) + (checksum >> 1) + *(((UINT8 *) tmp_bh->b_data)+(i+1)); |
| + |
| + if(skip) { |
| + PRINTK("skip from 0x%X ", index); |
| + index += uni; |
| + PRINTK("to 0x%X (amount of 0x%X)\n", index, uni); |
| + skip = FALSE; |
| + } else if(uni == index) |
| + index++; |
| + else if(uni == 0xFFFF) |
| + skip = TRUE; |
| + else { |
| + UINT16 col_index = get_col_index(index); |
| + |
| + if(upcase_table[col_index]== NULL) { |
| + PRINTK("alloc = 0x%X\n", col_index); |
| + upcase_table[col_index] = (UINT16 *) MALLOC(UTBL_ROW_COUNT * sizeof(UINT16)); |
| + if(upcase_table[col_index] == NULL) { |
| + ret = FFS_MEMORYERR; |
| + 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 FFS_SUCCESS; |
| + } |
| + ret = FFS_ERROR; |
| +error: |
| + if(tmp_bh) |
| + brelse(tmp_bh); |
| + free_upcase_table(sb); |
| + return ret; |
| +} |
| + |
| +INT32 __load_default_upcase_table(struct super_block *sb) |
| +{ |
| + INT32 i, ret = FFS_ERROR; |
| + UINT32 j; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + UINT8 skip = FALSE; |
| + UINT32 index = 0; |
| + UINT16 uni = 0; |
| + UINT16 **upcase_table; |
| + |
| + upcase_table = p_fs->vol_utbl = (UINT16 **) MALLOC(UTBL_COL_COUNT * sizeof(UINT16 *)); |
| + if(upcase_table == NULL) |
| + return FFS_MEMORYERR; |
| + MEMSET(upcase_table, 0, UTBL_COL_COUNT * sizeof(UINT16 *)); |
| + |
| + for(i = 0; index <= 0xFFFF && i < NUM_UPCASE*2; i += 2) { |
| + uni = GET16(uni_upcase + i); |
| + if(skip) { |
| + PRINTK("skip from 0x%X ", index); |
| + index += uni; |
| + PRINTK("to 0x%X (amount of 0x%X)\n", index, uni); |
| + skip = FALSE; |
| + } else if(uni == index) |
| + index++; |
| + else if(uni == 0xFFFF) |
| + skip = TRUE; |
| + else { |
| + UINT16 col_index = get_col_index(index); |
| + |
| + if(upcase_table[col_index]== NULL) { |
| + PRINTK("alloc = 0x%X\n", col_index); |
| + upcase_table[col_index] = (UINT16 *) MALLOC(UTBL_ROW_COUNT * sizeof(UINT16)); |
| + if(upcase_table[col_index] == NULL) { |
| + ret = FFS_MEMORYERR; |
| + 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 FFS_SUCCESS; |
| + |
| +error: |
| + free_upcase_table(sb); |
| + return ret; |
| +} |
| + |
| +INT32 load_upcase_table(struct super_block *sb) |
| +{ |
| + INT32 i; |
| + UINT32 tbl_clu, tbl_size; |
| + UINT32 type, sector, num_sectors; |
| + CHAIN_T clu; |
| + CASE_DENTRY_T *ep; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + 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 FFS_MEDIAERR; |
| + |
| + while (clu.dir != CLUSTER_32(~0)) { |
| + for (i = 0; i < p_fs->dentries_per_clu; i++) { |
| + ep = (CASE_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); |
| + if (!ep) |
| + return FFS_MEDIAERR; |
| + |
| + type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); |
| + |
| + if (type == TYPE_UNUSED) |
| + break; |
| + if (type != TYPE_UPCASE) |
| + continue; |
| + |
| + tbl_clu = GET32_A(ep->start_clu); |
| + tbl_size = (UINT32) 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)) != FFS_SUCCESS) |
| + break; |
| + else |
| + return FFS_SUCCESS; |
| + } |
| + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) |
| + return FFS_MEDIAERR; |
| + } |
| + return __load_default_upcase_table(sb); |
| +} |
| + |
| +void free_upcase_table(struct super_block *sb) |
| +{ |
| + UINT32 i; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + UINT16 **upcase_table; |
| + |
| + upcase_table = p_fs->vol_utbl; |
| + for(i = 0 ; i < UTBL_COL_COUNT ; i ++) |
| + FREE(upcase_table[i]); |
| + |
| + FREE(p_fs->vol_utbl); |
| + |
| + p_fs->vol_utbl = NULL; |
| +} |
| + |
| +UINT32 fat_get_entry_type(DENTRY_T *p_entry) |
| +{ |
| + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; |
| + |
| + if (*(ep->name) == 0x0) |
| + return TYPE_UNUSED; |
| + |
| + else if (*(ep->name) == 0xE5) |
| + return TYPE_DELETED; |
| + |
| + else if (ep->attr == ATTR_EXTEND) |
| + return TYPE_EXTEND; |
| + |
| + else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_VOLUME) |
| + return TYPE_VOLUME; |
| + |
| + else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_SUBDIR) |
| + return TYPE_DIR; |
| + |
| + return TYPE_FILE; |
| +} |
| + |
| +UINT32 exfat_get_entry_type(DENTRY_T *p_entry) |
| +{ |
| + FILE_DENTRY_T *ep = (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; |
| +} |
| + |
| +void fat_set_entry_type(DENTRY_T *p_entry, UINT32 type) |
| +{ |
| + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; |
| + |
| + if (type == TYPE_UNUSED) |
| + *(ep->name) = 0x0; |
| + |
| + else if (type == TYPE_DELETED) |
| + *(ep->name) = 0xE5; |
| + |
| + else if (type == TYPE_EXTEND) |
| + ep->attr = ATTR_EXTEND; |
| + |
| + else if (type == TYPE_DIR) |
| + ep->attr = ATTR_SUBDIR; |
| + |
| + else if (type == TYPE_FILE) |
| + ep->attr = ATTR_ARCHIVE; |
| + |
| + else if (type == TYPE_SYMLINK) |
| + ep->attr = ATTR_ARCHIVE | ATTR_SYMLINK; |
| +} |
| + |
| +void exfat_set_entry_type(DENTRY_T *p_entry, UINT32 type) |
| +{ |
| + FILE_DENTRY_T *ep = (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); |
| + } |
| +} |
| + |
| +UINT32 fat_get_entry_attr(DENTRY_T *p_entry) |
| +{ |
| + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; |
| + return((UINT32) ep->attr); |
| +} |
| + |
| +UINT32 exfat_get_entry_attr(DENTRY_T *p_entry) |
| +{ |
| + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; |
| + return((UINT32) GET16_A(ep->attr)); |
| +} |
| + |
| +void fat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr) |
| +{ |
| + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; |
| + ep->attr = (UINT8) attr; |
| +} |
| + |
| +void exfat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr) |
| +{ |
| + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; |
| + SET16_A(ep->attr, (UINT16) attr); |
| +} |
| + |
| +UINT8 fat_get_entry_flag(DENTRY_T *p_entry) |
| +{ |
| + return 0x01; |
| +} |
| + |
| +UINT8 exfat_get_entry_flag(DENTRY_T *p_entry) |
| +{ |
| + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; |
| + return(ep->flags); |
| +} |
| + |
| +void fat_set_entry_flag(DENTRY_T *p_entry, UINT8 flags) |
| +{ |
| +} |
| + |
| +void exfat_set_entry_flag(DENTRY_T *p_entry, UINT8 flags) |
| +{ |
| + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; |
| + ep->flags = flags; |
| +} |
| + |
| +UINT32 fat_get_entry_clu0(DENTRY_T *p_entry) |
| +{ |
| + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; |
| + return((GET32_A(ep->start_clu_hi) << 16) | GET16_A(ep->start_clu_lo)); |
| +} |
| + |
| +UINT32 exfat_get_entry_clu0(DENTRY_T *p_entry) |
| +{ |
| + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; |
| + return(GET32_A(ep->start_clu)); |
| +} |
| + |
| +void fat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu) |
| +{ |
| + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; |
| + SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu)); |
| + SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16)); |
| +} |
| + |
| +void exfat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu) |
| +{ |
| + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; |
| + SET32_A(ep->start_clu, start_clu); |
| +} |
| + |
| +UINT64 fat_get_entry_size(DENTRY_T *p_entry) |
| +{ |
| + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; |
| + return((UINT64) GET32_A(ep->size)); |
| +} |
| + |
| +UINT64 exfat_get_entry_size(DENTRY_T *p_entry) |
| +{ |
| + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; |
| + return(GET64_A(ep->valid_size)); |
| +} |
| + |
| +void fat_set_entry_size(DENTRY_T *p_entry, UINT64 size) |
| +{ |
| + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; |
| + SET32_A(ep->size, (UINT32) size); |
| +} |
| + |
| +void exfat_set_entry_size(DENTRY_T *p_entry, UINT64 size) |
| +{ |
| + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; |
| + SET64_A(ep->valid_size, size); |
| + SET64_A(ep->size, size); |
| +} |
| + |
| +void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) |
| +{ |
| + UINT16 t = 0x00, d = 0x21; |
| + DOS_DENTRY_T *ep = (DOS_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; |
| + } |
| + |
| + 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_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) |
| +{ |
| + UINT16 t = 0x00, d = 0x21; |
| + FILE_DENTRY_T *ep = (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 fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) |
| +{ |
| + UINT16 t, d; |
| + DOS_DENTRY_T *ep = (DOS_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; |
| + } |
| +} |
| + |
| +void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) |
| +{ |
| + UINT16 t, d; |
| + FILE_DENTRY_T *ep = (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; |
| + } |
| +} |
| + |
| +INT32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, |
| + UINT32 start_clu, UINT64 size) |
| +{ |
| + UINT32 sector; |
| + DOS_DENTRY_T *dos_ep; |
| + |
| + dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); |
| + if (!dos_ep) |
| + return FFS_MEDIAERR; |
| + |
| + init_dos_entry(dos_ep, type, start_clu); |
| + buf_modify(sb, sector); |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, |
| + UINT32 start_clu, UINT64 size) |
| +{ |
| + UINT32 sector; |
| + UINT8 flags; |
| + FILE_DENTRY_T *file_ep; |
| + STRM_DENTRY_T *strm_ep; |
| + |
| + flags = (type == TYPE_FILE) ? 0x01 : 0x03; |
| + |
| + file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); |
| + if (!file_ep) |
| + return FFS_MEDIAERR; |
| + |
| + strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, §or); |
| + if (!strm_ep) |
| + return FFS_MEDIAERR; |
| + |
| + init_file_entry(file_ep, type); |
| + buf_modify(sb, sector); |
| + |
| + init_strm_entry(strm_ep, flags, start_clu, size); |
| + buf_modify(sb, sector); |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 fat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, |
| + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) |
| +{ |
| + INT32 i; |
| + UINT32 sector; |
| + UINT8 chksum; |
| + UINT16 *uniname = p_uniname->name; |
| + DOS_DENTRY_T *dos_ep; |
| + EXT_DENTRY_T *ext_ep; |
| + |
| + dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); |
| + if (!dos_ep) |
| + return FFS_MEDIAERR; |
| + |
| + dos_ep->lcase = p_dosname->name_case; |
| + MEMCPY(dos_ep->name, p_dosname->name, DOS_NAME_LENGTH); |
| + buf_modify(sb, sector); |
| + |
| + if ((--num_entries) > 0) { |
| + chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); |
| + |
| + for (i = 1; i < num_entries; i++) { |
| + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); |
| + if (!ext_ep) |
| + return FFS_MEDIAERR; |
| + |
| + init_ext_entry(ext_ep, i, chksum, uniname); |
| + buf_modify(sb, sector); |
| + uniname += 13; |
| + } |
| + |
| + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); |
| + if (!ext_ep) |
| + return FFS_MEDIAERR; |
| + |
| + init_ext_entry(ext_ep, i+0x40, chksum, uniname); |
| + buf_modify(sb, sector); |
| + } |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 exfat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, |
| + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) |
| +{ |
| + INT32 i; |
| + UINT32 sector; |
| + UINT16 *uniname = p_uniname->name; |
| + FILE_DENTRY_T *file_ep; |
| + STRM_DENTRY_T *strm_ep; |
| + NAME_DENTRY_T *name_ep; |
| + |
| + file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); |
| + if (!file_ep) |
| + return FFS_MEDIAERR; |
| + |
| + file_ep->num_ext = (UINT8)(num_entries - 1); |
| + buf_modify(sb, sector); |
| + |
| + strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, §or); |
| + if (!strm_ep) |
| + return FFS_MEDIAERR; |
| + |
| + strm_ep->name_len = p_uniname->name_len; |
| + SET16_A(strm_ep->name_hash, p_uniname->name_hash); |
| + buf_modify(sb, sector); |
| + |
| + for (i = 2; i < num_entries; i++) { |
| + name_ep = (NAME_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+i, §or); |
| + if (!name_ep) |
| + return FFS_MEDIAERR; |
| + |
| + init_name_entry(name_ep, uniname); |
| + buf_modify(sb, sector); |
| + uniname += 15; |
| + } |
| + |
| + update_dir_checksum(sb, p_dir, entry); |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +void init_dos_entry(DOS_DENTRY_T *ep, UINT32 type, UINT32 start_clu) |
| +{ |
| + TIMESTAMP_T tm, *tp; |
| + |
| + fat_set_entry_type((DENTRY_T *) ep, type); |
| + SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu)); |
| + SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16)); |
| + SET32_A(ep->size, 0); |
| + |
| + tp = tm_current(&tm); |
| + fat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); |
| + fat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); |
| + SET16_A(ep->access_date, 0); |
| + ep->create_time_ms = 0; |
| +} |
| + |
| +void init_ext_entry(EXT_DENTRY_T *ep, INT32 order, UINT8 chksum, UINT16 *uniname) |
| +{ |
| + INT32 i; |
| + UINT8 end = FALSE; |
| + |
| + fat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); |
| + ep->order = (UINT8) order; |
| + ep->sysid = 0; |
| + ep->checksum = chksum; |
| + SET16_A(ep->start_clu, 0); |
| + |
| + for (i = 0; i < 10; i += 2) { |
| + if (!end) { |
| + SET16(ep->unicode_0_4+i, *uniname); |
| + if (*uniname == 0x0) |
| + end = TRUE; |
| + else |
| + uniname++; |
| + } else { |
| + SET16(ep->unicode_0_4+i, 0xFFFF); |
| + } |
| + } |
| + |
| + for (i = 0; i < 12; i += 2) { |
| + if (!end) { |
| + SET16_A(ep->unicode_5_10+i, *uniname); |
| + if (*uniname == 0x0) |
| + end = TRUE; |
| + else |
| + uniname++; |
| + } else { |
| + SET16_A(ep->unicode_5_10+i, 0xFFFF); |
| + } |
| + } |
| + |
| + for (i = 0; i < 4; i += 2) { |
| + if (!end) { |
| + SET16_A(ep->unicode_11_12+i, *uniname); |
| + if (*uniname == 0x0) |
| + end = TRUE; |
| + else |
| + uniname++; |
| + } else { |
| + SET16_A(ep->unicode_11_12+i, 0xFFFF); |
| + } |
| + } |
| +} |
| + |
| +void init_file_entry(FILE_DENTRY_T *ep, UINT32 type) |
| +{ |
| + TIMESTAMP_T tm, *tp; |
| + |
| + exfat_set_entry_type((DENTRY_T *) ep, type); |
| + |
| + tp = tm_current(&tm); |
| + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); |
| + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); |
| + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_ACCESS); |
| + ep->create_time_ms = 0; |
| + ep->modify_time_ms = 0; |
| + ep->access_time_ms = 0; |
| +} |
| + |
| +void init_strm_entry(STRM_DENTRY_T *ep, UINT8 flags, UINT32 start_clu, UINT64 size) |
| +{ |
| + exfat_set_entry_type((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); |
| +} |
| + |
| +void init_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname) |
| +{ |
| + INT32 i; |
| + |
| + exfat_set_entry_type((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++; |
| + } |
| +} |
| + |
| +void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries) |
| +{ |
| + INT32 i; |
| + UINT32 sector; |
| + DENTRY_T *ep; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + for (i = num_entries-1; i >= order; i--) { |
| + ep = get_entry_in_dir(sb, p_dir, entry-i, §or); |
| + if (!ep) |
| + return; |
| + |
| + p_fs->fs_func->set_entry_type(ep, TYPE_DELETED); |
| + buf_modify(sb, sector); |
| + } |
| +} |
| + |
| +void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries) |
| +{ |
| + INT32 i; |
| + UINT32 sector; |
| + DENTRY_T *ep; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + for (i = order; i < num_entries; i++) { |
| + ep = get_entry_in_dir(sb, p_dir, entry+i, §or); |
| + if (!ep) |
| + return; |
| + |
| + p_fs->fs_func->set_entry_type(ep, TYPE_DELETED); |
| + buf_modify(sb, sector); |
| + } |
| +} |
| + |
| +void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, INT32 entry) |
| +{ |
| + INT32 i, num_entries; |
| + UINT32 sector; |
| + UINT16 chksum; |
| + FILE_DENTRY_T *file_ep; |
| + DENTRY_T *ep; |
| + |
| + file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); |
| + if (!file_ep) |
| + return; |
| + |
| + buf_lock(sb, sector); |
| + |
| + num_entries = (INT32) 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) { |
| + buf_unlock(sb, sector); |
| + return; |
| + } |
| + |
| + chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, CS_DEFAULT); |
| + } |
| + |
| + SET16_A(file_ep->checksum, chksum); |
| + buf_modify(sb, sector); |
| + buf_unlock(sb, sector); |
| +} |
| + |
| +void update_dir_checksum_with_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es) |
| +{ |
| + DENTRY_T *ep; |
| + UINT16 chksum = 0; |
| + INT32 chksum_type = CS_DIR_ENTRY, i; |
| + |
| + ep = (DENTRY_T *)&(es->__buf); |
| + for (i=0; i < es->num_entries; i++) { |
| + PRINTK ("update_dir_checksum_with_entry_set ep %p\n", ep); |
| + chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, chksum_type); |
| + ep++; |
| + chksum_type = CS_DEFAULT; |
| + } |
| + |
| + ep = (DENTRY_T *)&(es->__buf); |
| + SET16_A(((FILE_DENTRY_T *)ep)->checksum, chksum); |
| + write_whole_entry_set(sb, es); |
| +} |
| + |
| +static INT32 _walk_fat_chain (struct super_block *sb, CHAIN_T *p_dir, INT32 byte_offset, UINT32 *clu) |
| +{ |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + INT32 clu_offset; |
| + UINT32 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 (FAT_read(sb, cur_clu, &cur_clu) == -1) |
| + return FFS_MEDIAERR; |
| + clu_offset--; |
| + } |
| + } |
| + |
| + if (clu) |
| + *clu = cur_clu; |
| + return FFS_SUCCESS; |
| +} |
| +INT32 find_location(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector, INT32 *offset) |
| +{ |
| + INT32 off, ret; |
| + UINT32 clu=0; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + |
| + off = entry << DENTRY_SIZE_BITS; |
| + |
| + if (p_dir->dir == CLUSTER_32(0)) { |
| + *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 != FFS_SUCCESS) |
| + return ret; |
| + |
| + off &= p_fs->cluster_size - 1; |
| + |
| + *offset = off & p_bd->sector_size_mask; |
| + *sector = off >> p_bd->sector_size_bits; |
| + *sector += START_SECTOR(clu); |
| + } |
| + return FFS_SUCCESS; |
| +} |
| + |
| +DENTRY_T *get_entry_with_sector(struct super_block *sb, UINT32 sector, INT32 offset) |
| +{ |
| + UINT8 *buf; |
| + |
| + buf = buf_getblk(sb, sector); |
| + |
| + if (buf == NULL) |
| + return NULL; |
| + |
| + return((DENTRY_T *)(buf + offset)); |
| +} |
| + |
| +DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector) |
| +{ |
| + INT32 off; |
| + UINT32 sec; |
| + UINT8 *buf; |
| + |
| + if (find_location(sb, p_dir, entry, &sec, &off) != FFS_SUCCESS) |
| + return NULL; |
| + |
| + buf = buf_getblk(sb, sec); |
| + |
| + if (buf == NULL) |
| + return NULL; |
| + |
| + if (sector != NULL) |
| + *sector = sec; |
| + return((DENTRY_T *)(buf + off)); |
| +} |
| + |
| +#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 |
| +ENTRY_SET_CACHE_T *get_entry_set_in_dir (struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, DENTRY_T **file_ep) |
| +{ |
| + INT32 off, ret, byte_offset; |
| + UINT32 clu=0; |
| + UINT32 sec, entry_type; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + ENTRY_SET_CACHE_T *es = NULL; |
| + DENTRY_T *ep, *pos; |
| + UINT8 *buf; |
| + UINT8 num_entries; |
| + INT32 mode = ES_MODE_STARTED; |
| + |
| + PRINTK("get_entry_set_in_dir entered\n"); |
| + PRINTK("p_dir dir %u flags %x size %d\n", 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 != FFS_SUCCESS) |
| + return NULL; |
| + |
| + |
| + byte_offset &= p_fs->cluster_size - 1; |
| + |
| + off = byte_offset & p_bd->sector_size_mask; |
| + sec = byte_offset >> p_bd->sector_size_bits; |
| + sec += START_SECTOR(clu); |
| + |
| + buf = buf_getblk(sb, sec); |
| + if (buf == NULL) |
| + goto err_out; |
| + |
| + |
| + ep = (DENTRY_T *)(buf + off); |
| + entry_type = p_fs->fs_func->get_entry_type(ep); |
| + |
| + if ((entry_type != TYPE_FILE) |
| + && (entry_type != TYPE_DIR)) |
| + goto err_out; |
| + |
| + if (type == ES_ALL_ENTRIES) |
| + num_entries = ((FILE_DENTRY_T *)ep)->num_ext+1; |
| + else |
| + num_entries = type; |
| + |
| + PRINTK("trying to malloc %x bytes for %d entries\n", offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T), num_entries); |
| + es = MALLOC(offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T)); |
| + if (es == NULL) |
| + goto err_out; |
| + |
| + es->num_entries = num_entries; |
| + es->sector = sec; |
| + es->offset = off; |
| + es->alloc_flag = p_dir->flags; |
| + |
| + pos = (DENTRY_T *) &(es->__buf); |
| + |
| + while(num_entries) { |
| + |
| + entry_type = p_fs->fs_func->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; |
| + } |
| + |
| + COPY_DENTRY(pos, ep); |
| + |
| + if (--num_entries == 0) |
| + break; |
| + |
| + if (((off + DENTRY_SIZE) & p_bd->sector_size_mask) < (off & p_bd->sector_size_mask)) { |
| + if (IS_LAST_SECTOR_IN_CLUSTER(sec)) { |
| + if (es->alloc_flag == 0x03) { |
| + clu++; |
| + } else { |
| + if (FAT_read(sb, clu, &clu) == -1) |
| + goto err_out; |
| + } |
| + sec = START_SECTOR(clu); |
| + } else { |
| + sec++; |
| + } |
| + buf = buf_getblk(sb, sec); |
| + if (buf == NULL) |
| + goto err_out; |
| + off = 0; |
| + ep = (DENTRY_T *)(buf); |
| + } else { |
| + ep++; |
| + off += DENTRY_SIZE; |
| + } |
| + pos++; |
| + } |
| + |
| + if (file_ep) |
| + *file_ep = (DENTRY_T *)&(es->__buf); |
| + |
| + PRINTK("es sec %u offset %d flags %d, num_entries %u buf ptr %p\n", |
| + es->sector, es->offset, es->alloc_flag, es->num_entries, &(es->__buf)); |
| + PRINTK("get_entry_set_in_dir exited %p\n", es); |
| + return es; |
| +err_out: |
| + PRINTK("get_entry_set_in_dir exited NULL (es %p)\n", es); |
| + if (es) |
| + FREE(es); |
| + return NULL; |
| +} |
| + |
| +void release_entry_set (ENTRY_SET_CACHE_T *es) |
| +{ |
| + PRINTK("release_entry_set %p\n", es); |
| + FREE(es); |
| +} |
| + |
| + |
| +static INT32 __write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, UINT32 sec, INT32 off, UINT32 count) |
| +{ |
| + INT32 num_entries, buf_off = (off - es->offset); |
| + UINT32 remaining_byte_in_sector, copy_entries; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + UINT32 clu; |
| + UINT8 *buf, *esbuf = (UINT8 *)&(es->__buf); |
| + |
| + PRINTK("__write_partial_entries_in_entry_set entered\n"); |
| + PRINTK("es %p sec %u off %d count %d\n", es, sec, off, count); |
| + num_entries = count; |
| + |
| + while(num_entries) { |
| + remaining_byte_in_sector = (1 << p_bd->sector_size_bits) - off; |
| + copy_entries = MIN(remaining_byte_in_sector>> DENTRY_SIZE_BITS , num_entries); |
| + buf = buf_getblk(sb, sec); |
| + if (buf == NULL) |
| + goto err_out; |
| + PRINTK("es->buf %p buf_off %u\n", esbuf, buf_off); |
| + PRINTK("copying %d entries from %p to sector %u\n", copy_entries, (esbuf + buf_off), sec); |
| + MEMCPY(buf + off, esbuf + buf_off, copy_entries << DENTRY_SIZE_BITS); |
| + buf_modify(sb, sec); |
| + num_entries -= copy_entries; |
| + |
| + if (num_entries) { |
| + if (IS_LAST_SECTOR_IN_CLUSTER(sec)) { |
| + clu = GET_CLUSTER_FROM_SECTOR(sec); |
| + if (es->alloc_flag == 0x03) { |
| + clu++; |
| + } else { |
| + if (FAT_read(sb, clu, &clu) == -1) |
| + goto err_out; |
| + } |
| + sec = START_SECTOR(clu); |
| + } else { |
| + sec++; |
| + } |
| + off = 0; |
| + buf_off += copy_entries << DENTRY_SIZE_BITS; |
| + } |
| + } |
| + |
| + PRINTK("__write_partial_entries_in_entry_set exited successfully\n"); |
| + return FFS_SUCCESS; |
| +err_out: |
| + PRINTK("__write_partial_entries_in_entry_set failed\n"); |
| + return FFS_ERROR; |
| +} |
| + |
| +INT32 write_whole_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es) |
| +{ |
| + return (__write_partial_entries_in_entry_set(sb, es, es->sector,es->offset, es->num_entries)); |
| +} |
| + |
| +INT32 write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, UINT32 count) |
| +{ |
| + INT32 ret, byte_offset, off; |
| + UINT32 clu=0, sec; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + CHAIN_T dir; |
| + |
| + if (ep + count > ((DENTRY_T *)&(es->__buf)) + es->num_entries) |
| + return FFS_ERROR; |
| + |
| + dir.dir = GET_CLUSTER_FROM_SECTOR(es->sector); |
| + dir.flags = es->alloc_flag; |
| + dir.size = 0xffffffff; |
| + |
| + byte_offset = (es->sector - START_SECTOR(dir.dir)) << p_bd->sector_size_bits; |
| + byte_offset += ((INT32)ep - (INT32)&(es->__buf)) + es->offset; |
| + |
| + ret =_walk_fat_chain(sb, &dir, byte_offset, &clu); |
| + if (ret != FFS_SUCCESS) |
| + return ret; |
| + byte_offset &= p_fs->cluster_size - 1; |
| + off = byte_offset & p_bd->sector_size_mask; |
| + sec = byte_offset >> p_bd->sector_size_bits; |
| + sec += START_SECTOR(clu); |
| + return (__write_partial_entries_in_entry_set(sb, es, sec, off, count)); |
| +} |
| + |
| +INT32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 num_entries) |
| +{ |
| + INT32 i, dentry, num_empty = 0; |
| + INT32 dentries_per_clu; |
| + UINT32 type; |
| + CHAIN_T clu; |
| + DENTRY_T *ep; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (p_dir->dir == CLUSTER_32(0)) |
| + 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)) |
| + 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 = p_fs->fs_func->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; |
| + |
| + if (clu.flags == 0x03) { |
| + if ((--clu.size) > 0) |
| + clu.dir++; |
| + else |
| + clu.dir = CLUSTER_32(~0); |
| + } else { |
| + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) |
| + return -1; |
| + } |
| + } |
| + |
| + return -1; |
| +} |
| + |
| +INT32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, INT32 num_entries) |
| +{ |
| + INT32 ret, dentry; |
| + UINT32 last_clu, sector; |
| + UINT64 size = 0; |
| + CHAIN_T clu; |
| + DENTRY_T *ep = NULL; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); |
| + |
| + if (p_dir->dir == CLUSTER_32(0)) |
| + 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_fs->vol_type == EXFAT) { |
| + 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; |
| + |
| + ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu); |
| + if (ret < 1) |
| + return -1; |
| + |
| + if (clear_cluster(sb, clu.dir) != FFS_SUCCESS) |
| + return -1; |
| + |
| + 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) |
| + FAT_write(sb, last_clu, clu.dir); |
| + |
| + 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++; |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + if (p_dir->dir != p_fs->root_dir) { |
| + size += p_fs->cluster_size; |
| + |
| + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry+1, §or); |
| + if (!ep) |
| + return -1; |
| + p_fs->fs_func->set_entry_size(ep, size); |
| + p_fs->fs_func->set_entry_flag(ep, p_dir->flags); |
| + 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); |
| +} |
| + |
| +INT32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type) |
| +{ |
| + INT32 i, dentry = 0, lossy = FALSE, len; |
| + INT32 order = 0, is_feasible_entry = TRUE, has_ext_entry = FALSE; |
| + INT32 dentries_per_clu; |
| + UINT32 entry_type; |
| + UINT16 entry_uniname[14], *uniname = NULL, unichar; |
| + CHAIN_T clu; |
| + DENTRY_T *ep; |
| + DOS_DENTRY_T *dos_ep; |
| + EXT_DENTRY_T *ext_ep; |
| + 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, (UINT16 *) UNI_CUR_DIR_NAME)) || |
| + (!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_PAR_DIR_NAME))) |
| + return -1; |
| + } |
| + |
| + if (p_dir->dir == CLUSTER_32(0)) |
| + dentries_per_clu = p_fs->dentries_in_root; |
| + else |
| + dentries_per_clu = p_fs->dentries_per_clu; |
| + |
| + clu.dir = p_dir->dir; |
| + 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++, dentry++) { |
| + ep = get_entry_in_dir(sb, &clu, i, NULL); |
| + if (!ep) |
| + return -2; |
| + |
| + entry_type = p_fs->fs_func->get_entry_type(ep); |
| + |
| + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) { |
| + if ((type == TYPE_ALL) || (type == entry_type)) { |
| + if (is_feasible_entry && has_ext_entry) |
| + return(dentry); |
| + |
| + dos_ep = (DOS_DENTRY_T *) ep; |
| + if ((!lossy) && (!nls_dosname_cmp(sb, p_dosname->name, dos_ep->name))) |
| + return(dentry); |
| + } |
| + is_feasible_entry = TRUE; |
| + has_ext_entry = FALSE; |
| + } else if (entry_type == TYPE_EXTEND) { |
| + if (is_feasible_entry) { |
| + ext_ep = (EXT_DENTRY_T *) ep; |
| + if (ext_ep->order > 0x40) { |
| + order = (INT32)(ext_ep->order - 0x40); |
| + uniname = p_uniname->name + 13 * (order-1); |
| + } else { |
| + order = (INT32) ext_ep->order; |
| + uniname -= 13; |
| + } |
| + |
| + len = extract_uni_name_from_ext_entry(ext_ep, entry_uniname, order); |
| + |
| + unichar = *(uniname+len); |
| + *(uniname+len) = 0x0; |
| + |
| + if (nls_uniname_cmp(sb, uniname, entry_uniname)) { |
| + is_feasible_entry = FALSE; |
| + } |
| + |
| + *(uniname+len) = unichar; |
| + } |
| + has_ext_entry = TRUE; |
| + } else if (entry_type == TYPE_UNUSED) { |
| + return -2; |
| + } else { |
| + is_feasible_entry = TRUE; |
| + has_ext_entry = FALSE; |
| + } |
| + } |
| + |
| + if (p_dir->dir == CLUSTER_32(0)) |
| + break; |
| + |
| + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) |
| + return -2; |
| + } |
| + |
| + return -2; |
| +} |
| + |
| +INT32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type) |
| +{ |
| + INT32 i, dentry = 0, num_ext_entries = 0, len; |
| + INT32 order = 0, is_feasible_entry = FALSE; |
| + INT32 dentries_per_clu, num_empty = 0; |
| + UINT32 entry_type; |
| + UINT16 entry_uniname[16], *uniname = NULL, unichar; |
| + CHAIN_T clu; |
| + DENTRY_T *ep; |
| + FILE_DENTRY_T *file_ep; |
| + STRM_DENTRY_T *strm_ep; |
| + NAME_DENTRY_T *name_ep; |
| + 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, (UINT16 *) UNI_CUR_DIR_NAME)) || |
| + (!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_PAR_DIR_NAME))) |
| + return -1; |
| + } |
| + |
| + if (p_dir->dir == CLUSTER_32(0)) |
| + 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; |
| + |
| + for (i = 0; i < dentries_per_clu; i++, dentry++) { |
| + ep = get_entry_in_dir(sb, &clu, i, NULL); |
| + if (!ep) |
| + return -2; |
| + |
| + entry_type = p_fs->fs_func->get_entry_type(ep); |
| + |
| + 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)) { |
| + if ((type == TYPE_ALL) || (type == entry_type)) { |
| + file_ep = (FILE_DENTRY_T *) ep; |
| + num_ext_entries = file_ep->num_ext; |
| + is_feasible_entry = TRUE; |
| + } else { |
| + is_feasible_entry = FALSE; |
| + } |
| + } else if (entry_type == TYPE_STREAM) { |
| + if (is_feasible_entry) { |
| + strm_ep = (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; |
| + } |
| + } |
| + } else if (entry_type == TYPE_EXTEND) { |
| + if (is_feasible_entry) { |
| + name_ep = (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; |
| + } 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; |
| + } |
| + } |
| + } |
| + |
| + if (p_dir->dir == CLUSTER_32(0)) |
| + break; |
| + |
| + if (clu.flags == 0x03) { |
| + if ((--clu.size) > 0) |
| + clu.dir++; |
| + else |
| + clu.dir = CLUSTER_32(~0); |
| + } else { |
| + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) |
| + return -2; |
| + } |
| + } |
| + |
| + return -2; |
| +} |
| + |
| +INT32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry) |
| +{ |
| + INT32 count = 0; |
| + UINT8 chksum; |
| + DOS_DENTRY_T *dos_ep = (DOS_DENTRY_T *) p_entry; |
| + EXT_DENTRY_T *ext_ep; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); |
| + |
| + for (entry--; entry >= 0; entry--) { |
| + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL); |
| + if (!ext_ep) |
| + return -1; |
| + |
| + if ((p_fs->fs_func->get_entry_type((DENTRY_T *) ext_ep) == TYPE_EXTEND) && |
| + (ext_ep->checksum == chksum)) { |
| + count++; |
| + if (ext_ep->order > 0x40) |
| + return(count); |
| + } else { |
| + return(count); |
| + } |
| + } |
| + |
| + return(count); |
| +} |
| + |
| +INT32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry) |
| +{ |
| + INT32 i, count = 0; |
| + UINT32 type; |
| + FILE_DENTRY_T *file_ep = (FILE_DENTRY_T *) p_entry; |
| + DENTRY_T *ext_ep; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + 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 = p_fs->fs_func->get_entry_type(ext_ep); |
| + if ((type == TYPE_EXTEND) || (type == TYPE_STREAM)) { |
| + count++; |
| + } else { |
| + return(count); |
| + } |
| + } |
| + |
| + return(count); |
| +} |
| + |
| +INT32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, UINT32 type) |
| +{ |
| + INT32 i, count = 0; |
| + INT32 dentries_per_clu; |
| + UINT32 entry_type; |
| + CHAIN_T clu; |
| + DENTRY_T *ep; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (p_dir->dir == CLUSTER_32(0)) |
| + 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 -1; |
| + |
| + entry_type = p_fs->fs_func->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; |
| + |
| + if (clu.flags == 0x03) { |
| + if ((--clu.size) > 0) |
| + clu.dir++; |
| + else |
| + clu.dir = CLUSTER_32(~0); |
| + } else { |
| + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) |
| + return -1; |
| + } |
| + } |
| + |
| + return(count); |
| +} |
| + |
| +BOOL is_dir_empty(struct super_block *sb, CHAIN_T *p_dir) |
| +{ |
| + INT32 i, count = 0; |
| + INT32 dentries_per_clu; |
| + UINT32 type; |
| + CHAIN_T clu; |
| + DENTRY_T *ep; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (p_dir->dir == CLUSTER_32(0)) |
| + 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 = p_fs->fs_func->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)) { |
| + return FALSE; |
| + } else { |
| + 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; |
| + |
| + if (clu.flags == 0x03) { |
| + if ((--clu.size) > 0) |
| + clu.dir++; |
| + else |
| + clu.dir = CLUSTER_32(~0); |
| + } else { |
| + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) |
| + break; |
| + } |
| + } |
| + |
| + return TRUE; |
| +} |
| + |
| +INT32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 *entries, DOS_NAME_T *p_dosname) |
| +{ |
| + INT32 ret, num_entries, lossy = FALSE; |
| + INT8 **r; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + num_entries = p_fs->fs_func->calc_num_entries(p_uniname); |
| + if (num_entries == 0) |
| + return FFS_INVALIDPATH; |
| + |
| + if (p_fs->vol_type != EXFAT) { |
| + nls_uniname_to_dosname(sb, p_dosname, p_uniname, &lossy); |
| + |
| + if (lossy) { |
| + ret = fat_generate_dos_name(sb, p_dir, p_dosname); |
| + if (ret) |
| + return ret; |
| + } else { |
| + for (r = reserved_names; *r; r++) { |
| + if (!STRNCMP((void *) p_dosname->name, *r, 8)) |
| + return FFS_INVALIDPATH; |
| + } |
| + |
| + if (p_dosname->name_case != 0xFF) |
| + num_entries = 1; |
| + } |
| + |
| + if (num_entries > 1) |
| + p_dosname->name_case = 0x0; |
| + } |
| + |
| + *entries = num_entries; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, UINT8 mode) |
| +{ |
| + DOS_NAME_T dos_name; |
| + |
| + if (mode == 0x0) |
| + dos_name.name_case = 0x0; |
| + else |
| + dos_name.name_case = ep->lcase; |
| + |
| + MEMCPY(dos_name.name, ep->name, DOS_NAME_LENGTH); |
| + nls_dosname_to_uniname(sb, p_uniname, &dos_name); |
| +} |
| + |
| +void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname) |
| +{ |
| + INT32 i; |
| + EXT_DENTRY_T *ep; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + for (entry--, i = 1; entry >= 0; entry--, i++) { |
| + ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL); |
| + if (!ep) |
| + return; |
| + |
| + if (p_fs->fs_func->get_entry_type((DENTRY_T *) ep) == TYPE_EXTEND) { |
| + extract_uni_name_from_ext_entry(ep, uniname, i); |
| + if (ep->order > 0x40) |
| + return; |
| + } else { |
| + return; |
| + } |
| + |
| + uniname += 13; |
| + } |
| +} |
| + |
| +void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname) |
| +{ |
| + INT32 i; |
| + DENTRY_T *ep; |
| + ENTRY_SET_CACHE_T *es; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + es = get_entry_set_in_dir(sb, p_dir, entry, ES_ALL_ENTRIES, &ep); |
| + if (es == NULL || es->num_entries < 3) { |
| + if(es) { |
| + release_entry_set(es); |
| + } |
| + return; |
| + } |
| + |
| + ep += 2; |
| + |
| + for (i = 2; i < es->num_entries; i++, ep++) { |
| + if (p_fs->fs_func->get_entry_type(ep) == TYPE_EXTEND) { |
| + extract_uni_name_from_name_entry((NAME_DENTRY_T *)ep, uniname, i); |
| + } else { |
| + goto out; |
| + } |
| + uniname += 15; |
| + } |
| + |
| +out: |
| + release_entry_set(es); |
| +} |
| + |
| +INT32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, UINT16 *uniname, INT32 order) |
| +{ |
| + INT32 i, len = 0; |
| + |
| + for (i = 0; i < 10; i += 2) { |
| + *uniname = GET16(ep->unicode_0_4+i); |
| + if (*uniname == 0x0) |
| + return(len); |
| + uniname++; |
| + len++; |
| + } |
| + |
| + if (order < 20) { |
| + for (i = 0; i < 12; i += 2) { |
| + *uniname = GET16_A(ep->unicode_5_10+i); |
| + if (*uniname == 0x0) |
| + return(len); |
| + uniname++; |
| + len++; |
| + } |
| + } else { |
| + for (i = 0; i < 8; i += 2) { |
| + *uniname = GET16_A(ep->unicode_5_10+i); |
| + if (*uniname == 0x0) |
| + return(len); |
| + uniname++; |
| + len++; |
| + } |
| + *uniname = 0x0; |
| + return(len); |
| + } |
| + |
| + for (i = 0; i < 4; i += 2) { |
| + *uniname = GET16_A(ep->unicode_11_12+i); |
| + if (*uniname == 0x0) |
| + return(len); |
| + uniname++; |
| + len++; |
| + } |
| + |
| + *uniname = 0x0; |
| + return(len); |
| + |
| +} |
| + |
| +INT32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname, INT32 order) |
| +{ |
| + INT32 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); |
| + |
| +} |
| + |
| +INT32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname) |
| +{ |
| + INT32 i, j, count = 0, count_begin = FALSE; |
| + INT32 dentries_per_clu; |
| + UINT32 type; |
| + UINT8 bmap[128]; |
| + CHAIN_T clu; |
| + DOS_DENTRY_T *ep; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + Bitmap_clear_all(bmap, 128); |
| + Bitmap_set(bmap, 0); |
| + |
| + if (p_dir->dir == CLUSTER_32(0)) |
| + dentries_per_clu = p_fs->dentries_in_root; |
| + else |
| + dentries_per_clu = p_fs->dentries_per_clu; |
| + |
| + clu.dir = p_dir->dir; |
| + 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 = (DOS_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); |
| + if (!ep) |
| + return FFS_MEDIAERR; |
| + |
| + type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); |
| + |
| + if (type == TYPE_UNUSED) |
| + break; |
| + if ((type != TYPE_FILE) && (type != TYPE_DIR)) |
| + continue; |
| + |
| + count = 0; |
| + count_begin = FALSE; |
| + |
| + for (j = 0; j < 8; j++) { |
| + if (ep->name[j] == ' ') |
| + break; |
| + |
| + if (ep->name[j] == '~') { |
| + count_begin = TRUE; |
| + } else if (count_begin) { |
| + if ((ep->name[j] >= '0') && (ep->name[j] <= '9')) { |
| + count = count * 10 + (ep->name[j] - '0'); |
| + } else { |
| + count = 0; |
| + count_begin = FALSE; |
| + } |
| + } |
| + } |
| + |
| + if ((count > 0) && (count < 1024)) |
| + Bitmap_set(bmap, count); |
| + } |
| + |
| + if (p_dir->dir == CLUSTER_32(0)) |
| + break; |
| + |
| + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) |
| + return FFS_MEDIAERR; |
| + } |
| + |
| + count = 0; |
| + for (i = 0; i < 128; i++) { |
| + if (bmap[i] != 0xFF) { |
| + for (j = 0; j < 8; j++) { |
| + if (Bitmap_test(&(bmap[i]), j) == 0) { |
| + count = (i << 3) + j; |
| + break; |
| + } |
| + } |
| + if (count != 0) |
| + break; |
| + } |
| + } |
| + |
| + if ((count == 0) || (count >= 1024)) |
| + return FFS_FILEEXIST; |
| + else |
| + fat_attach_count_to_dos_name(p_dosname->name, count); |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +void fat_attach_count_to_dos_name(UINT8 *dosname, INT32 count) |
| +{ |
| + INT32 i, j, length; |
| + INT8 str_count[6]; |
| + |
| + str_count[0] = '~'; |
| + str_count[1] = '\0'; |
| + my_itoa(&(str_count[1]), count); |
| + length = STRLEN(str_count); |
| + |
| + i = j = 0; |
| + while (j <= (8 - length)) { |
| + i = j; |
| + if (dosname[j] == ' ') |
| + break; |
| + if (dosname[j] & 0x80) |
| + j += 2; |
| + else |
| + j++; |
| + } |
| + |
| + for (j = 0; j < length; i++, j++) |
| + dosname[i] = (UINT8) str_count[j]; |
| + |
| + if (i == 7) |
| + dosname[7] = ' '; |
| + |
| +} |
| + |
| +INT32 fat_calc_num_entries(UNI_NAME_T *p_uniname) |
| +{ |
| + INT32 len; |
| + |
| + len = p_uniname->name_len; |
| + if (len == 0) |
| + return 0; |
| + |
| + return((len-1) / 13 + 2); |
| + |
| +} |
| + |
| +INT32 exfat_calc_num_entries(UNI_NAME_T *p_uniname) |
| +{ |
| + INT32 len; |
| + |
| + len = p_uniname->name_len; |
| + if (len == 0) |
| + return 0; |
| + |
| + return((len-1) / 15 + 3); |
| + |
| +} |
| + |
| +UINT8 calc_checksum_1byte(void *data, INT32 len, UINT8 chksum) |
| +{ |
| + INT32 i; |
| + UINT8 *c = (UINT8 *) data; |
| + |
| + for (i = 0; i < len; i++, c++) |
| + chksum = (((chksum & 1) << 7) | ((chksum & 0xFE) >> 1)) + *c; |
| + |
| + return(chksum); |
| +} |
| + |
| +UINT16 calc_checksum_2byte(void *data, INT32 len, UINT16 chksum, INT32 type) |
| +{ |
| + INT32 i; |
| + UINT8 *c = (UINT8 *) 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)) + (UINT16) *c; |
| + } |
| + break; |
| + default |
| + : |
| + for (i = 0; i < len; i++, c++) { |
| + chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (UINT16) *c; |
| + } |
| + } |
| + |
| + return(chksum); |
| +} |
| + |
| +UINT32 calc_checksum_4byte(void *data, INT32 len, UINT32 chksum, INT32 type) |
| +{ |
| + INT32 i; |
| + UINT8 *c = (UINT8 *) data; |
| + |
| + switch (type) { |
| + case CS_PBR_SECTOR: |
| + for (i = 0; i < len; i++, c++) { |
| + if ((i == 106) || (i == 107) || (i == 112)) |
| + continue; |
| + chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (UINT32) *c; |
| + } |
| + break; |
| + default |
| + : |
| + for (i = 0; i < len; i++, c++) { |
| + chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (UINT32) *c; |
| + } |
| + } |
| + |
| + return(chksum); |
| +} |
| + |
| + |
| +INT32 resolve_path(struct inode *inode, UINT8 *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname) |
| +{ |
| + INT32 lossy = FALSE; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); |
| + |
| + if (STRLEN(path) >= (MAX_NAME_LENGTH * MAX_CHARSET_SIZE)) |
| + return(FFS_INVALIDPATH); |
| + |
| + STRCPY(name_buf, path); |
| + |
| + nls_cstring_to_uniname(sb, p_uniname, name_buf, &lossy); |
| + if (lossy) |
| + return(FFS_INVALIDPATH); |
| + |
| + fid->size = i_size_read(inode); |
| + |
| + p_dir->dir = fid->start_clu; |
| + p_dir->size = (INT32)(fid->size >> p_fs->cluster_size_bits); |
| + p_dir->flags = fid->flags; |
| + |
| + return(FFS_SUCCESS); |
| +} |
| + |
| + |
| +static FS_FUNC_T fat_fs_func = { |
| + .alloc_cluster = fat_alloc_cluster, |
| + .free_cluster = fat_free_cluster, |
| + .count_used_clusters = fat_count_used_clusters, |
| + |
| + .init_dir_entry = fat_init_dir_entry, |
| + .init_ext_entry = fat_init_ext_entry, |
| + .find_dir_entry = fat_find_dir_entry, |
| + .delete_dir_entry = fat_delete_dir_entry, |
| + .get_uni_name_from_ext_entry = fat_get_uni_name_from_ext_entry, |
| + .count_ext_entries = fat_count_ext_entries, |
| + .calc_num_entries = fat_calc_num_entries, |
| + |
| + .get_entry_type = fat_get_entry_type, |
| + .set_entry_type = fat_set_entry_type, |
| + .get_entry_attr = fat_get_entry_attr, |
| + .set_entry_attr = fat_set_entry_attr, |
| + .get_entry_flag = fat_get_entry_flag, |
| + .set_entry_flag = fat_set_entry_flag, |
| + .get_entry_clu0 = fat_get_entry_clu0, |
| + .set_entry_clu0 = fat_set_entry_clu0, |
| + .get_entry_size = fat_get_entry_size, |
| + .set_entry_size = fat_set_entry_size, |
| + .get_entry_time = fat_get_entry_time, |
| + .set_entry_time = fat_set_entry_time, |
| +}; |
| + |
| + |
| +INT32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) |
| +{ |
| + INT32 num_reserved, num_root_sectors; |
| + BPB16_T *p_bpb = (BPB16_T *) p_pbr->bpb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + |
| + if (p_bpb->num_fats == 0) |
| + return FFS_FORMATERR; |
| + |
| + num_root_sectors = GET16(p_bpb->num_root_entries) << DENTRY_SIZE_BITS; |
| + num_root_sectors = ((num_root_sectors-1) >> p_bd->sector_size_bits) + 1; |
| + |
| + p_fs->sectors_per_clu = p_bpb->sectors_per_clu; |
| + p_fs->sectors_per_clu_bits = my_log2(p_bpb->sectors_per_clu); |
| + 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 = GET16(p_bpb->num_fat_sectors); |
| + |
| + p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved); |
| + 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->FAT2_start_sector + p_fs->num_FAT_sectors; |
| + p_fs->data_start_sector = p_fs->root_start_sector + num_root_sectors; |
| + |
| + p_fs->num_sectors = GET16(p_bpb->num_sectors); |
| + if (p_fs->num_sectors == 0) |
| + p_fs->num_sectors = GET32(p_bpb->num_huge_sectors); |
| + |
| + num_reserved = p_fs->data_start_sector - p_fs->PBR_sector; |
| + p_fs->num_clusters = ((p_fs->num_sectors - num_reserved) >> p_fs->sectors_per_clu_bits) + 2; |
| + |
| + if (p_fs->num_clusters < FAT12_THRESHOLD) |
| + p_fs->vol_type = FAT12; |
| + else |
| + p_fs->vol_type = FAT16; |
| + p_fs->vol_id = GET32(p_bpb->vol_serial); |
| + |
| + p_fs->root_dir = 0; |
| + p_fs->dentries_in_root = GET16(p_bpb->num_root_entries); |
| + p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); |
| + |
| + p_fs->vol_flag = VOL_CLEAN; |
| + p_fs->clu_srch_ptr = 2; |
| + p_fs->used_clusters = (UINT32) ~0; |
| + |
| + p_fs->fs_func = &fat_fs_func; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) |
| +{ |
| + INT32 num_reserved; |
| + BPB32_T *p_bpb = (BPB32_T *) p_pbr->bpb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + |
| + if (p_bpb->num_fats == 0) |
| + return FFS_FORMATERR; |
| + |
| + p_fs->sectors_per_clu = p_bpb->sectors_per_clu; |
| + p_fs->sectors_per_clu_bits = my_log2(p_bpb->sectors_per_clu); |
| + 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->num_fat32_sectors); |
| + |
| + p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved); |
| + 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->FAT2_start_sector + p_fs->num_FAT_sectors; |
| + p_fs->data_start_sector = p_fs->root_start_sector; |
| + |
| + p_fs->num_sectors = GET32(p_bpb->num_huge_sectors); |
| + num_reserved = p_fs->data_start_sector - p_fs->PBR_sector; |
| + |
| + p_fs->num_clusters = ((p_fs->num_sectors-num_reserved) >> p_fs->sectors_per_clu_bits) + 2; |
| + |
| + p_fs->vol_type = FAT32; |
| + 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 = VOL_CLEAN; |
| + p_fs->clu_srch_ptr = 2; |
| + p_fs->used_clusters = (UINT32) ~0; |
| + |
| + p_fs->fs_func = &fat_fs_func; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +static FS_FUNC_T exfat_fs_func = { |
| + .alloc_cluster = exfat_alloc_cluster, |
| + .free_cluster = exfat_free_cluster, |
| + .count_used_clusters = exfat_count_used_clusters, |
| + |
| + .init_dir_entry = exfat_init_dir_entry, |
| + .init_ext_entry = exfat_init_ext_entry, |
| + .find_dir_entry = exfat_find_dir_entry, |
| + .delete_dir_entry = exfat_delete_dir_entry, |
| + .get_uni_name_from_ext_entry = exfat_get_uni_name_from_ext_entry, |
| + .count_ext_entries = exfat_count_ext_entries, |
| + .calc_num_entries = exfat_calc_num_entries, |
| + |
| + .get_entry_type = exfat_get_entry_type, |
| + .set_entry_type = exfat_set_entry_type, |
| + .get_entry_attr = exfat_get_entry_attr, |
| + .set_entry_attr = exfat_set_entry_attr, |
| + .get_entry_flag = exfat_get_entry_flag, |
| + .set_entry_flag = exfat_set_entry_flag, |
| + .get_entry_clu0 = exfat_get_entry_clu0, |
| + .set_entry_clu0 = exfat_set_entry_clu0, |
| + .get_entry_size = exfat_get_entry_size, |
| + .set_entry_size = exfat_set_entry_size, |
| + .get_entry_time = exfat_get_entry_time, |
| + .set_entry_time = exfat_set_entry_time, |
| +}; |
| + |
| +INT32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) |
| +{ |
| + BPBEX_T *p_bpb = (BPBEX_T *) p_pbr->bpb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + |
| + if (p_bpb->num_fats == 0) |
| + return FFS_FORMATERR; |
| + |
| + 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; |
| + |
| + 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 = (UINT32) GET16(p_bpb->vol_flags); |
| + p_fs->clu_srch_ptr = 2; |
| + p_fs->used_clusters = (UINT32) ~0; |
| + |
| + p_fs->fs_func = &exfat_fs_func; |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) |
| +{ |
| + INT32 ret, dentry, num_entries; |
| + UINT64 size; |
| + CHAIN_T clu; |
| + DOS_NAME_T dos_name, dot_name; |
| + struct super_block *sb = inode->i_sb; |
| + 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; |
| + |
| + dentry = find_empty_entry(inode, p_dir, num_entries); |
| + if (dentry < 0) |
| + return FFS_FULL; |
| + |
| + clu.dir = CLUSTER_32(~0); |
| + clu.size = 0; |
| + clu.flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; |
| + |
| + ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu); |
| + if (ret < 1) |
| + return FFS_FULL; |
| + |
| + ret = clear_cluster(sb, clu.dir); |
| + if (ret != FFS_SUCCESS) |
| + return ret; |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + size = p_fs->cluster_size; |
| + } else { |
| + size = 0; |
| + |
| + dot_name.name_case = 0x0; |
| + MEMCPY(dot_name.name, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH); |
| + |
| + ret = p_fs->fs_func->init_dir_entry(sb, &clu, 0, TYPE_DIR, clu.dir, 0); |
| + if (ret != FFS_SUCCESS) |
| + return ret; |
| + |
| + ret = p_fs->fs_func->init_ext_entry(sb, &clu, 0, 1, NULL, &dot_name); |
| + if (ret != FFS_SUCCESS) |
| + return ret; |
| + |
| + MEMCPY(dot_name.name, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH); |
| + |
| + if (p_dir->dir == p_fs->root_dir) |
| + ret = p_fs->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, CLUSTER_32(0), 0); |
| + else |
| + ret = p_fs->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, p_dir->dir, 0); |
| + |
| + if (ret != FFS_SUCCESS) |
| + return ret; |
| + |
| + ret = p_fs->fs_func->init_ext_entry(sb, &clu, 1, 1, NULL, &dot_name); |
| + if (ret != FFS_SUCCESS) |
| + return ret; |
| + } |
| + |
| + ret = p_fs->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_DIR, clu.dir, size); |
| + if (ret != FFS_SUCCESS) |
| + return ret; |
| + |
| + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); |
| + if (ret != FFS_SUCCESS) |
| + 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 FFS_SUCCESS; |
| +} |
| + |
| +INT32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, UINT8 mode, FILE_ID_T *fid) |
| +{ |
| + INT32 ret, dentry, num_entries; |
| + DOS_NAME_T dos_name; |
| + struct super_block *sb = inode->i_sb; |
| + 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; |
| + |
| + dentry = find_empty_entry(inode, p_dir, num_entries); |
| + if (dentry < 0) |
| + return FFS_FULL; |
| + |
| + ret = p_fs->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_FILE | mode, CLUSTER_32(0), 0); |
| + if (ret != FFS_SUCCESS) |
| + return ret; |
| + |
| + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); |
| + if (ret != FFS_SUCCESS) |
| + 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 FFS_SUCCESS; |
| +} |
| + |
| +void remove_file(struct inode *inode, CHAIN_T *p_dir, INT32 entry) |
| +{ |
| + INT32 num_entries; |
| + UINT32 sector; |
| + DENTRY_T *ep; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + ep = get_entry_in_dir(sb, p_dir, entry, §or); |
| + if (!ep) |
| + return; |
| + |
| + buf_lock(sb, sector); |
| + |
| + num_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, entry, ep); |
| + if (num_entries < 0) { |
| + buf_unlock(sb, sector); |
| + return; |
| + } |
| + num_entries++; |
| + |
| + buf_unlock(sb, sector); |
| + |
| + p_fs->fs_func->delete_dir_entry(sb, p_dir, entry, 0, num_entries); |
| +} |
| + |
| +INT32 rename_file(struct inode *inode, CHAIN_T *p_dir, INT32 oldentry, UNI_NAME_T *p_uniname, FILE_ID_T *fid) |
| +{ |
| + INT32 ret, newentry = -1, num_old_entries, num_new_entries; |
| + UINT32 sector_old, sector_new; |
| + DOS_NAME_T dos_name; |
| + DENTRY_T *epold, *epnew; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + epold = get_entry_in_dir(sb, p_dir, oldentry, §or_old); |
| + if (!epold) |
| + return FFS_MEDIAERR; |
| + |
| + buf_lock(sb, sector_old); |
| + |
| + num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, oldentry, epold); |
| + if (num_old_entries < 0) { |
| + buf_unlock(sb, sector_old); |
| + return FFS_MEDIAERR; |
| + } |
| + num_old_entries++; |
| + |
| + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_new_entries, &dos_name); |
| + if (ret) { |
| + 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) { |
| + buf_unlock(sb, sector_old); |
| + return FFS_FULL; |
| + } |
| + |
| + epnew = get_entry_in_dir(sb, p_dir, newentry, §or_new); |
| + if (!epnew) { |
| + buf_unlock(sb, sector_old); |
| + return FFS_MEDIAERR; |
| + } |
| + |
| + MEMCPY((void *) epnew, (void *) epold, DENTRY_SIZE); |
| + if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) { |
| + p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); |
| + fid->attr |= ATTR_ARCHIVE; |
| + } |
| + buf_modify(sb, sector_new); |
| + buf_unlock(sb, sector_old); |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + epold = get_entry_in_dir(sb, p_dir, oldentry+1, §or_old); |
| + buf_lock(sb, sector_old); |
| + epnew = get_entry_in_dir(sb, p_dir, newentry+1, §or_new); |
| + |
| + if (!epold || !epnew) { |
| + buf_unlock(sb, sector_old); |
| + return FFS_MEDIAERR; |
| + } |
| + |
| + MEMCPY((void *) epnew, (void *) epold, DENTRY_SIZE); |
| + buf_modify(sb, sector_new); |
| + buf_unlock(sb, sector_old); |
| + } |
| + |
| + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, newentry, num_new_entries, p_uniname, &dos_name); |
| + if (ret != FFS_SUCCESS) |
| + return ret; |
| + |
| + p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, 0, num_old_entries); |
| + fid->entry = newentry; |
| + } else { |
| + if (p_fs->fs_func->get_entry_type(epold) == TYPE_FILE) { |
| + p_fs->fs_func->set_entry_attr(epold, p_fs->fs_func->get_entry_attr(epold) | ATTR_ARCHIVE); |
| + fid->attr |= ATTR_ARCHIVE; |
| + } |
| + buf_modify(sb, sector_old); |
| + buf_unlock(sb, sector_old); |
| + |
| + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, oldentry, num_new_entries, p_uniname, &dos_name); |
| + if (ret != FFS_SUCCESS) |
| + return ret; |
| + |
| + p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, num_new_entries, num_old_entries); |
| + } |
| + |
| + return FFS_SUCCESS; |
| +} |
| + |
| +INT32 move_file(struct inode *inode, CHAIN_T *p_olddir, INT32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) |
| +{ |
| + INT32 ret, newentry, num_new_entries, num_old_entries; |
| + UINT32 sector_mov, sector_new; |
| + CHAIN_T clu; |
| + DOS_NAME_T dos_name; |
| + DENTRY_T *epmov, *epnew; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + epmov = get_entry_in_dir(sb, p_olddir, oldentry, §or_mov); |
| + if (!epmov) |
| + return FFS_MEDIAERR; |
| + |
| + if (p_fs->fs_func->get_entry_type(epmov) == TYPE_DIR && |
| + p_fs->fs_func->get_entry_clu0(epmov) == p_newdir->dir) |
| + return FFS_INVALIDPATH; |
| + |
| + buf_lock(sb, sector_mov); |
| + |
| + num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_olddir, oldentry, epmov); |
| + if (num_old_entries < 0) { |
| + buf_unlock(sb, sector_mov); |
| + return FFS_MEDIAERR; |
| + } |
| + num_old_entries++; |
| + |
| + ret = get_num_entries_and_dos_name(sb, p_newdir, p_uniname, &num_new_entries, &dos_name); |
| + if (ret) { |
| + buf_unlock(sb, sector_mov); |
| + return ret; |
| + } |
| + |
| + newentry = find_empty_entry(inode, p_newdir, num_new_entries); |
| + if (newentry < 0) { |
| + buf_unlock(sb, sector_mov); |
| + return FFS_FULL; |
| + } |
| + |
| + epnew = get_entry_in_dir(sb, p_newdir, newentry, §or_new); |
| + if (!epnew) { |
| + buf_unlock(sb, sector_mov); |
| + return FFS_MEDIAERR; |
| + } |
| + |
| + MEMCPY((void *) epnew, (void *) epmov, DENTRY_SIZE); |
| + if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) { |
| + p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); |
| + fid->attr |= ATTR_ARCHIVE; |
| + } |
| + buf_modify(sb, sector_new); |
| + buf_unlock(sb, sector_mov); |
| + |
| + if (p_fs->vol_type == EXFAT) { |
| + epmov = get_entry_in_dir(sb, p_olddir, oldentry+1, §or_mov); |
| + buf_lock(sb, sector_mov); |
| + epnew = get_entry_in_dir(sb, p_newdir, newentry+1, §or_new); |
| + if (!epmov || !epnew) { |
| + buf_unlock(sb, sector_mov); |
| + return FFS_MEDIAERR; |
| + } |
| + |
| + MEMCPY((void *) epnew, (void *) epmov, DENTRY_SIZE); |
| + buf_modify(sb, sector_new); |
| + buf_unlock(sb, sector_mov); |
| + } else if (p_fs->fs_func->get_entry_type(epnew) == TYPE_DIR) { |
| + clu.dir = p_fs->fs_func->get_entry_clu0(epnew); |
| + clu.flags = 0x01; |
| + |
| + epnew = get_entry_in_dir(sb, &clu, 1, §or_new); |
| + if (!epnew) |
| + return FFS_MEDIAERR; |
| + |
| + if (p_newdir->dir == p_fs->root_dir) |
| + p_fs->fs_func->set_entry_clu0(epnew, CLUSTER_32(0)); |
| + else |
| + p_fs->fs_func->set_entry_clu0(epnew, p_newdir->dir); |
| + buf_modify(sb, sector_new); |
| + } |
| + |
| + ret = p_fs->fs_func->init_ext_entry(sb, p_newdir, newentry, num_new_entries, p_uniname, &dos_name); |
| + if (ret != FFS_SUCCESS) |
| + return ret; |
| + |
| + p_fs->fs_func->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 FFS_SUCCESS; |
| +} |
| + |
| +INT32 sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 read) |
| +{ |
| + INT32 ret = FFS_MEDIAERR; |
| + 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)) { |
| + PRINT("[EXFAT] sector_read: out of range error! (sec = %d)\n", sec); |
| + fs_error(sb); |
| + return ret; |
| + } |
| + |
| + if (!p_fs->dev_ejected) { |
| + ret = bdev_read(sb, sec, bh, 1, read); |
| + if (ret != FFS_SUCCESS) |
| + p_fs->dev_ejected = TRUE; |
| + } |
| + |
| + return ret; |
| +} |
| + |
| +INT32 sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 sync) |
| +{ |
| + INT32 ret = FFS_MEDIAERR; |
| + 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)) { |
| + PRINT("[EXFAT] sector_write: out of range error! (sec = %d)\n", sec); |
| + fs_error(sb); |
| + return ret; |
| + } |
| + |
| + if (bh == NULL) { |
| + PRINT("[EXFAT] sector_write: bh is NULL!\n"); |
| + fs_error(sb); |
| + return ret; |
| + } |
| + |
| + if (!p_fs->dev_ejected) { |
| + ret = bdev_write(sb, sec, bh, 1, sync); |
| + if (ret != FFS_SUCCESS) |
| + p_fs->dev_ejected = TRUE; |
| + } |
| + |
| + return ret; |
| +} |
| + |
| +INT32 multi_sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 num_secs, INT32 read) |
| +{ |
| + INT32 ret = FFS_MEDIAERR; |
| + 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)) { |
| + PRINT("[EXFAT] multi_sector_read: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs); |
| + fs_error(sb); |
| + return ret; |
| + } |
| + |
| + if (!p_fs->dev_ejected) { |
| + ret = bdev_read(sb, sec, bh, num_secs, read); |
| + if (ret != FFS_SUCCESS) |
| + p_fs->dev_ejected = TRUE; |
| + } |
| + |
| + return ret; |
| +} |
| + |
| +INT32 multi_sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 num_secs, INT32 sync) |
| +{ |
| + INT32 ret = FFS_MEDIAERR; |
| + 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)) { |
| + PRINT("[EXFAT] multi_sector_write: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs); |
| + fs_error(sb); |
| + return ret; |
| + } |
| + if (bh == NULL) { |
| + PRINT("[EXFAT] multi_sector_write: bh is NULL!\n"); |
| + fs_error(sb); |
| + return ret; |
| + } |
| + |
| + if (!p_fs->dev_ejected) { |
| + ret = bdev_write(sb, sec, bh, num_secs, sync); |
| + if (ret != FFS_SUCCESS) |
| + p_fs->dev_ejected = TRUE; |
| + } |
| + |
| + return ret; |
| +} |
| diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h |
| new file mode 100644 |
| index 00000000..a10f4894 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat.h |
| @@ -0,0 +1,598 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#ifndef _EXFAT_H |
| +#define _EXFAT_H |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#include "exfat_data.h" |
| +#include "exfat_oal.h" |
| + |
| +#include "exfat_blkdev.h" |
| +#include "exfat_cache.h" |
| +#include "exfat_nls.h" |
| +#include "exfat_api.h" |
| +#include "exfat_cache.h" |
| + |
| +#ifdef __cplusplus |
| +extern "C" { |
| +#endif |
| + |
| +#if EXFAT_CONFIG_KERNEL_DEBUG |
| +#define EXFAT_IOC_GET_DEBUGFLAGS _IOR('f', 100, long) |
| +#define EXFAT_IOC_SET_DEBUGFLAGS _IOW('f', 101, long) |
| + |
| +#define EXFAT_DEBUGFLAGS_INVALID_UMOUNT 0x01 |
| +#define EXFAT_DEBUGFLAGS_ERROR_RW 0x02 |
| +#endif |
| + |
| +#define MAX_VOLUME 4 |
| + |
| +#define DENTRY_SIZE 32 |
| +#define DENTRY_SIZE_BITS 5 |
| + |
| +#define PBR_SIGNATURE 0xAA55 |
| +#define EXT_SIGNATURE 0xAA550000 |
| +#define VOL_LABEL "NO NAME " |
| +#define OEM_NAME "MSWIN4.1" |
| +#define STR_FAT12 "FAT12 " |
| +#define STR_FAT16 "FAT16 " |
| +#define STR_FAT32 "FAT32 " |
| +#define STR_EXFAT "EXFAT " |
| +#define VOL_CLEAN 0x0000 |
| +#define VOL_DIRTY 0x0002 |
| + |
| +#define FAT12_THRESHOLD 4087 |
| +#define FAT16_THRESHOLD 65527 |
| +#define FAT32_THRESHOLD 268435457 |
| +#define EXFAT_THRESHOLD 268435457 |
| + |
| +#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_SYMLINK 0x015F |
| +#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 TM_CREATE 0 |
| +#define TM_MODIFY 1 |
| +#define TM_ACCESS 2 |
| + |
| +#define CS_DIR_ENTRY 0 |
| +#define CS_PBR_SECTOR 1 |
| +#define CS_DEFAULT 2 |
| + |
| +#define CLUSTER_16(x) ((UINT16)(x)) |
| +#define CLUSTER_32(x) ((UINT32)(x)) |
| + |
| +#define START_SECTOR(x) \ |
| + ( (((x)-2) << p_fs->sectors_per_clu_bits) + p_fs->data_start_sector ) |
| + |
| +#define IS_LAST_SECTOR_IN_CLUSTER(sec) \ |
| + ( (((sec) - p_fs->data_start_sector + 1) & ((1 << p_fs->sectors_per_clu_bits) -1)) == 0) |
| + |
| +#define GET_CLUSTER_FROM_SECTOR(sec) \ |
| + ((((sec) - p_fs->data_start_sector) >> p_fs->sectors_per_clu_bits) +2) |
| + |
| +#define GET16(p_src) \ |
| + ( ((UINT16)(p_src)[0]) | (((UINT16)(p_src)[1]) << 8) ) |
| +#define GET32(p_src) \ |
| + ( ((UINT32)(p_src)[0]) | (((UINT32)(p_src)[1]) << 8) | \ |
| + (((UINT32)(p_src)[2]) << 16) | (((UINT32)(p_src)[3]) << 24) ) |
| +#define GET64(p_src) \ |
| + ( ((UINT64)(p_src)[0]) | (((UINT64)(p_src)[1]) << 8) | \ |
| + (((UINT64)(p_src)[2]) << 16) | (((UINT64)(p_src)[3]) << 24) | \ |
| + (((UINT64)(p_src)[4]) << 32) | (((UINT64)(p_src)[5]) << 40) | \ |
| + (((UINT64)(p_src)[6]) << 48) | (((UINT64)(p_src)[7]) << 56) ) |
| + |
| + |
| +#define SET16(p_dst,src) \ |
| + do { \ |
| + (p_dst)[0]=(UINT8)(src); \ |
| + (p_dst)[1]=(UINT8)(((UINT16)(src)) >> 8); \ |
| + } while (0) |
| +#define SET32(p_dst,src) \ |
| + do { \ |
| + (p_dst)[0]=(UINT8)(src); \ |
| + (p_dst)[1]=(UINT8)(((UINT32)(src)) >> 8); \ |
| + (p_dst)[2]=(UINT8)(((UINT32)(src)) >> 16); \ |
| + (p_dst)[3]=(UINT8)(((UINT32)(src)) >> 24); \ |
| + } while (0) |
| +#define SET64(p_dst,src) \ |
| + do { \ |
| + (p_dst)[0]=(UINT8)(src); \ |
| + (p_dst)[1]=(UINT8)(((UINT64)(src)) >> 8); \ |
| + (p_dst)[2]=(UINT8)(((UINT64)(src)) >> 16); \ |
| + (p_dst)[3]=(UINT8)(((UINT64)(src)) >> 24); \ |
| + (p_dst)[4]=(UINT8)(((UINT64)(src)) >> 32); \ |
| + (p_dst)[5]=(UINT8)(((UINT64)(src)) >> 40); \ |
| + (p_dst)[6]=(UINT8)(((UINT64)(src)) >> 48); \ |
| + (p_dst)[7]=(UINT8)(((UINT64)(src)) >> 56); \ |
| + } while (0) |
| + |
| +#if (FFS_CONFIG_LITTLE_ENDIAN == 1) |
| +#define GET16_A(p_src) (*((UINT16 *)(p_src))) |
| +#define GET32_A(p_src) (*((UINT32 *)(p_src))) |
| +#define GET64_A(p_src) (*((UINT64 *)(p_src))) |
| +#define SET16_A(p_dst,src) *((UINT16 *)(p_dst)) = (UINT16)(src) |
| +#define SET32_A(p_dst,src) *((UINT32 *)(p_dst)) = (UINT32)(src) |
| +#define SET64_A(p_dst,src) *((UINT64 *)(p_dst)) = (UINT64)(src) |
| +#else |
| +#define GET16_A(p_src) GET16(p_src) |
| +#define GET32_A(p_src) GET32(p_src) |
| +#define GET64_A(p_src) GET64(p_src) |
| +#define SET16_A(p_dst,src) SET16(p_dst, src) |
| +#define SET32_A(p_dst,src) SET32(p_dst, src) |
| +#define SET64_A(p_dst,src) SET64(p_dst, src) |
| +#endif |
| + |
| +#define HIGH_INDEX_BIT (8) |
| +#define HIGH_INDEX_MASK (0xFF00) |
| +#define LOW_INDEX_BIT (16-HIGH_INDEX_BIT) |
| +#define UTBL_ROW_COUNT (1<<LOW_INDEX_BIT) |
| +#define UTBL_COL_COUNT (1<<HIGH_INDEX_BIT) |
| + |
| + static inline UINT16 get_col_index(UINT16 i) |
| + { |
| + return i >> LOW_INDEX_BIT; |
| + } |
| + static inline UINT16 get_row_index(UINT16 i) |
| + { |
| + return i & ~HIGH_INDEX_MASK; |
| + } |
| + |
| + typedef struct { |
| + UINT8 jmp_boot[3]; |
| + UINT8 oem_name[8]; |
| + UINT8 bpb[109]; |
| + UINT8 boot_code[390]; |
| + UINT8 signature[2]; |
| + } PBR_SECTOR_T; |
| + |
| + typedef struct { |
| + UINT8 sector_size[2]; |
| + UINT8 sectors_per_clu; |
| + UINT8 num_reserved[2]; |
| + UINT8 num_fats; |
| + UINT8 num_root_entries[2]; |
| + UINT8 num_sectors[2]; |
| + UINT8 media_type; |
| + UINT8 num_fat_sectors[2]; |
| + UINT8 sectors_in_track[2]; |
| + UINT8 num_heads[2]; |
| + UINT8 num_hid_sectors[4]; |
| + UINT8 num_huge_sectors[4]; |
| + |
| + UINT8 phy_drv_no; |
| + UINT8 reserved; |
| + UINT8 ext_signature; |
| + UINT8 vol_serial[4]; |
| + UINT8 vol_label[11]; |
| + UINT8 vol_type[8]; |
| + } BPB16_T; |
| + |
| + typedef struct { |
| + UINT8 sector_size[2]; |
| + UINT8 sectors_per_clu; |
| + UINT8 num_reserved[2]; |
| + UINT8 num_fats; |
| + UINT8 num_root_entries[2]; |
| + UINT8 num_sectors[2]; |
| + UINT8 media_type; |
| + UINT8 num_fat_sectors[2]; |
| + UINT8 sectors_in_track[2]; |
| + UINT8 num_heads[2]; |
| + UINT8 num_hid_sectors[4]; |
| + UINT8 num_huge_sectors[4]; |
| + UINT8 num_fat32_sectors[4]; |
| + UINT8 ext_flags[2]; |
| + UINT8 fs_version[2]; |
| + UINT8 root_cluster[4]; |
| + UINT8 fsinfo_sector[2]; |
| + UINT8 backup_sector[2]; |
| + UINT8 reserved[12]; |
| + |
| + UINT8 phy_drv_no; |
| + UINT8 ext_reserved; |
| + UINT8 ext_signature; |
| + UINT8 vol_serial[4]; |
| + UINT8 vol_label[11]; |
| + UINT8 vol_type[8]; |
| + } BPB32_T; |
| + |
| + typedef struct { |
| + UINT8 reserved1[53]; |
| + UINT8 vol_offset[8]; |
| + UINT8 vol_length[8]; |
| + UINT8 fat_offset[4]; |
| + UINT8 fat_length[4]; |
| + UINT8 clu_offset[4]; |
| + UINT8 clu_count[4]; |
| + UINT8 root_cluster[4]; |
| + UINT8 vol_serial[4]; |
| + UINT8 fs_version[2]; |
| + UINT8 vol_flags[2]; |
| + UINT8 sector_size_bits; |
| + UINT8 sectors_per_clu_bits; |
| + UINT8 num_fats; |
| + UINT8 phy_drv_no; |
| + UINT8 perc_in_use; |
| + UINT8 reserved2[7]; |
| + } BPBEX_T; |
| + |
| + typedef struct { |
| + UINT8 signature1[4]; |
| + UINT8 reserved1[480]; |
| + UINT8 signature2[4]; |
| + UINT8 free_cluster[4]; |
| + UINT8 next_cluster[4]; |
| + UINT8 reserved2[14]; |
| + UINT8 signature3[2]; |
| + } FSI_SECTOR_T; |
| + |
| + typedef struct { |
| + UINT8 dummy[32]; |
| + } DENTRY_T; |
| + |
| + typedef struct { |
| + UINT8 name[DOS_NAME_LENGTH]; |
| + UINT8 attr; |
| + UINT8 lcase; |
| + UINT8 create_time_ms; |
| + UINT8 create_time[2]; |
| + UINT8 create_date[2]; |
| + UINT8 access_date[2]; |
| + UINT8 start_clu_hi[2]; |
| + UINT8 modify_time[2]; |
| + UINT8 modify_date[2]; |
| + UINT8 start_clu_lo[2]; |
| + UINT8 size[4]; |
| + } DOS_DENTRY_T; |
| + |
| + typedef struct { |
| + UINT8 order; |
| + UINT8 unicode_0_4[10]; |
| + UINT8 attr; |
| + UINT8 sysid; |
| + UINT8 checksum; |
| + UINT8 unicode_5_10[12]; |
| + UINT8 start_clu[2]; |
| + UINT8 unicode_11_12[4]; |
| + } EXT_DENTRY_T; |
| + |
| + typedef struct { |
| + UINT8 type; |
| + UINT8 num_ext; |
| + UINT8 checksum[2]; |
| + UINT8 attr[2]; |
| + UINT8 reserved1[2]; |
| + UINT8 create_time[2]; |
| + UINT8 create_date[2]; |
| + UINT8 modify_time[2]; |
| + UINT8 modify_date[2]; |
| + UINT8 access_time[2]; |
| + UINT8 access_date[2]; |
| + UINT8 create_time_ms; |
| + UINT8 modify_time_ms; |
| + UINT8 access_time_ms; |
| + UINT8 reserved2[9]; |
| + } FILE_DENTRY_T; |
| + |
| + typedef struct { |
| + UINT8 type; |
| + UINT8 flags; |
| + UINT8 reserved1; |
| + UINT8 name_len; |
| + UINT8 name_hash[2]; |
| + UINT8 reserved2[2]; |
| + UINT8 valid_size[8]; |
| + UINT8 reserved3[4]; |
| + UINT8 start_clu[4]; |
| + UINT8 size[8]; |
| + } STRM_DENTRY_T; |
| + |
| + typedef struct { |
| + UINT8 type; |
| + UINT8 flags; |
| + UINT8 unicode_0_14[30]; |
| + } NAME_DENTRY_T; |
| + |
| + typedef struct { |
| + UINT8 type; |
| + UINT8 flags; |
| + UINT8 reserved[18]; |
| + UINT8 start_clu[4]; |
| + UINT8 size[8]; |
| + } BMAP_DENTRY_T; |
| + |
| + typedef struct { |
| + UINT8 type; |
| + UINT8 reserved1[3]; |
| + UINT8 checksum[4]; |
| + UINT8 reserved2[12]; |
| + UINT8 start_clu[4]; |
| + UINT8 size[8]; |
| + } CASE_DENTRY_T; |
| + |
| + typedef struct { |
| + UINT8 type; |
| + UINT8 label_len; |
| + UINT8 unicode_0_10[22]; |
| + UINT8 reserved[8]; |
| + } VOLM_DENTRY_T; |
| + |
| + typedef struct { |
| + UINT32 dir; |
| + INT32 entry; |
| + CHAIN_T clu; |
| + } UENTRY_T; |
| + |
| + typedef struct __FS_STRUCT_T { |
| + UINT32 mounted; |
| + struct super_block *sb; |
| + struct semaphore v_sem; |
| + } FS_STRUCT_T; |
| + |
| + typedef struct { |
| + INT32 (*alloc_cluster)(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain); |
| + void (*free_cluster)(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse); |
| + INT32 (*count_used_clusters)(struct super_block *sb); |
| + |
| + INT32 (*init_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, |
| + UINT32 start_clu, UINT64 size); |
| + INT32 (*init_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, |
| + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); |
| + INT32 (*find_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type); |
| + void (*delete_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 offset, INT32 num_entries); |
| + void (*get_uni_name_from_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname); |
| + INT32 (*count_ext_entries)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry); |
| + INT32 (*calc_num_entries)(UNI_NAME_T *p_uniname); |
| + |
| + UINT32 (*get_entry_type)(DENTRY_T *p_entry); |
| + void (*set_entry_type)(DENTRY_T *p_entry, UINT32 type); |
| + UINT32 (*get_entry_attr)(DENTRY_T *p_entry); |
| + void (*set_entry_attr)(DENTRY_T *p_entry, UINT32 attr); |
| + UINT8 (*get_entry_flag)(DENTRY_T *p_entry); |
| + void (*set_entry_flag)(DENTRY_T *p_entry, UINT8 flag); |
| + UINT32 (*get_entry_clu0)(DENTRY_T *p_entry); |
| + void (*set_entry_clu0)(DENTRY_T *p_entry, UINT32 clu0); |
| + UINT64 (*get_entry_size)(DENTRY_T *p_entry); |
| + void (*set_entry_size)(DENTRY_T *p_entry, UINT64 size); |
| + void (*get_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); |
| + void (*set_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); |
| + } FS_FUNC_T; |
| + |
| + typedef struct __FS_INFO_T { |
| + UINT32 drv; |
| + UINT32 vol_type; |
| + UINT32 vol_id; |
| + |
| + UINT32 num_sectors; |
| + UINT32 num_clusters; |
| + UINT32 cluster_size; |
| + UINT32 cluster_size_bits; |
| + UINT32 sectors_per_clu; |
| + UINT32 sectors_per_clu_bits; |
| + |
| + UINT32 PBR_sector; |
| + UINT32 FAT1_start_sector; |
| + UINT32 FAT2_start_sector; |
| + UINT32 root_start_sector; |
| + UINT32 data_start_sector; |
| + UINT32 num_FAT_sectors; |
| + |
| + UINT32 root_dir; |
| + UINT32 dentries_in_root; |
| + UINT32 dentries_per_clu; |
| + |
| + UINT32 vol_flag; |
| + struct buffer_head *pbr_bh; |
| + |
| + UINT32 map_clu; |
| + UINT32 map_sectors; |
| + struct buffer_head **vol_amap; |
| + |
| + UINT16 **vol_utbl; |
| + |
| + UINT32 clu_srch_ptr; |
| + UINT32 used_clusters; |
| + UENTRY_T hint_uentry; |
| + |
| + UINT32 dev_ejected; |
| + |
| + FS_FUNC_T *fs_func; |
| + |
| + BUF_CACHE_T FAT_cache_array[FAT_CACHE_SIZE]; |
| + BUF_CACHE_T FAT_cache_lru_list; |
| + BUF_CACHE_T FAT_cache_hash_list[FAT_CACHE_HASH_SIZE]; |
| + |
| + BUF_CACHE_T buf_cache_array[BUF_CACHE_SIZE]; |
| + BUF_CACHE_T buf_cache_lru_list; |
| + BUF_CACHE_T buf_cache_hash_list[BUF_CACHE_HASH_SIZE]; |
| + } FS_INFO_T; |
| + |
| +#define ES_2_ENTRIES 2 |
| +#define ES_3_ENTRIES 3 |
| +#define ES_ALL_ENTRIES 0 |
| + |
| + typedef struct { |
| + UINT32 sector; |
| + INT32 offset; |
| + INT32 alloc_flag; |
| + UINT32 num_entries; |
| + |
| + void *__buf; |
| + } ENTRY_SET_CACHE_T; |
| + |
| + INT32 ffsInit(void); |
| + INT32 ffsShutdown(void); |
| + |
| + INT32 ffsMountVol(struct super_block *sb, INT32 drv); |
| + INT32 ffsUmountVol(struct super_block *sb); |
| + INT32 ffsCheckVol(struct super_block *sb); |
| + INT32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info); |
| + INT32 ffsSyncVol(struct super_block *sb, INT32 do_sync); |
| + |
| + INT32 ffsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid); |
| + INT32 ffsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid); |
| + INT32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount); |
| + INT32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount); |
| + INT32 ffsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size); |
| + INT32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); |
| + INT32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid); |
| + INT32 ffsSetAttr(struct inode *inode, UINT32 attr); |
| + INT32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info); |
| + INT32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info); |
| + INT32 ffsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu); |
| + |
| + INT32 ffsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid); |
| + INT32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_ent); |
| + INT32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid); |
| + |
| + INT32 fs_init(void); |
| + INT32 fs_shutdown(void); |
| + void fs_set_vol_flags(struct super_block *sb, UINT32 new_flag); |
| + void fs_sync(struct super_block *sb, INT32 do_sync); |
| + void fs_error(struct super_block *sb); |
| + |
| + INT32 clear_cluster(struct super_block *sb, UINT32 clu); |
| + INT32 fat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain); |
| + INT32 exfat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain); |
| + void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse); |
| + void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse); |
| + UINT32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain); |
| + INT32 count_num_clusters(struct super_block *sb, CHAIN_T *dir); |
| + INT32 fat_count_used_clusters(struct super_block *sb); |
| + INT32 exfat_count_used_clusters(struct super_block *sb); |
| + void exfat_chain_cont_cluster(struct super_block *sb, UINT32 chain, INT32 len); |
| + |
| + INT32 load_alloc_bitmap(struct super_block *sb); |
| + void free_alloc_bitmap(struct super_block *sb); |
| + INT32 set_alloc_bitmap(struct super_block *sb, UINT32 clu); |
| + INT32 clr_alloc_bitmap(struct super_block *sb, UINT32 clu); |
| + UINT32 test_alloc_bitmap(struct super_block *sb, UINT32 clu); |
| + void sync_alloc_bitmap(struct super_block *sb); |
| + |
| + INT32 load_upcase_table(struct super_block *sb); |
| + void free_upcase_table(struct super_block *sb); |
| + |
| + UINT32 fat_get_entry_type(DENTRY_T *p_entry); |
| + UINT32 exfat_get_entry_type(DENTRY_T *p_entry); |
| + void fat_set_entry_type(DENTRY_T *p_entry, UINT32 type); |
| + void exfat_set_entry_type(DENTRY_T *p_entry, UINT32 type); |
| + UINT32 fat_get_entry_attr(DENTRY_T *p_entry); |
| + UINT32 exfat_get_entry_attr(DENTRY_T *p_entry); |
| + void fat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr); |
| + void exfat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr); |
| + UINT8 fat_get_entry_flag(DENTRY_T *p_entry); |
| + UINT8 exfat_get_entry_flag(DENTRY_T *p_entry); |
| + void fat_set_entry_flag(DENTRY_T *p_entry, UINT8 flag); |
| + void exfat_set_entry_flag(DENTRY_T *p_entry, UINT8 flag); |
| + UINT32 fat_get_entry_clu0(DENTRY_T *p_entry); |
| + UINT32 exfat_get_entry_clu0(DENTRY_T *p_entry); |
| + void fat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu); |
| + void exfat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu); |
| + UINT64 fat_get_entry_size(DENTRY_T *p_entry); |
| + UINT64 exfat_get_entry_size(DENTRY_T *p_entry); |
| + void fat_set_entry_size(DENTRY_T *p_entry, UINT64 size); |
| + void exfat_set_entry_size(DENTRY_T *p_entry, UINT64 size); |
| + void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); |
| + void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); |
| + void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); |
| + void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); |
| + INT32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, UINT32 start_clu, UINT64 size); |
| + INT32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, UINT32 start_clu, UINT64 size); |
| + INT32 fat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); |
| + INT32 exfat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); |
| + void init_dos_entry(DOS_DENTRY_T *ep, UINT32 type, UINT32 start_clu); |
| + void init_ext_entry(EXT_DENTRY_T *ep, INT32 order, UINT8 chksum, UINT16 *uniname); |
| + void init_file_entry(FILE_DENTRY_T *ep, UINT32 type); |
| + void init_strm_entry(STRM_DENTRY_T *ep, UINT8 flags, UINT32 start_clu, UINT64 size); |
| + void init_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname); |
| + void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries); |
| + void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries); |
| + |
| + INT32 find_location(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector, INT32 *offset); |
| + DENTRY_T *get_entry_with_sector(struct super_block *sb, UINT32 sector, INT32 offset); |
| + DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector); |
| + ENTRY_SET_CACHE_T *get_entry_set_in_dir (struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, DENTRY_T **file_ep); |
| + void release_entry_set (ENTRY_SET_CACHE_T *es); |
| + INT32 write_whole_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es); |
| + INT32 write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, UINT32 count); |
| + INT32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 num_entries); |
| + INT32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, INT32 num_entries); |
| + INT32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type); |
| + INT32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type); |
| + INT32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry); |
| + INT32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry); |
| + INT32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, UINT32 type); |
| + void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, INT32 entry); |
| + void update_dir_checksum_with_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es); |
| + BOOL is_dir_empty(struct super_block *sb, CHAIN_T *p_dir); |
| + |
| + INT32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 *entries, DOS_NAME_T *p_dosname); |
| + void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, UINT8 mode); |
| + void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname); |
| + void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname); |
| + INT32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, UINT16 *uniname, INT32 order); |
| + INT32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname, INT32 order); |
| + INT32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname); |
| + void fat_attach_count_to_dos_name(UINT8 *dosname, INT32 count); |
| + INT32 fat_calc_num_entries(UNI_NAME_T *p_uniname); |
| + INT32 exfat_calc_num_entries(UNI_NAME_T *p_uniname); |
| + UINT8 calc_checksum_1byte(void *data, INT32 len, UINT8 chksum); |
| + UINT16 calc_checksum_2byte(void *data, INT32 len, UINT16 chksum, INT32 type); |
| + UINT32 calc_checksum_4byte(void *data, INT32 len, UINT32 chksum, INT32 type); |
| + |
| + INT32 resolve_path(struct inode *inode, UINT8 *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname); |
| + INT32 resolve_name(UINT8 *name, UINT8 **arg); |
| + |
| + INT32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); |
| + INT32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); |
| + INT32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); |
| + INT32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid); |
| + INT32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, UINT8 mode, FILE_ID_T *fid); |
| + void remove_file(struct inode *inode, CHAIN_T *p_dir, INT32 entry); |
| + INT32 rename_file(struct inode *inode, CHAIN_T *p_dir, INT32 old_entry, UNI_NAME_T *p_uniname, FILE_ID_T *fid); |
| + INT32 move_file(struct inode *inode, CHAIN_T *p_olddir, INT32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid); |
| + |
| + INT32 sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 read); |
| + INT32 sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 sync); |
| + INT32 multi_sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 num_secs, INT32 read); |
| + INT32 multi_sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 num_secs, INT32 sync); |
| + |
| +#ifdef __cplusplus |
| +} |
| +#endif |
| + |
| +#endif |
| diff --git a/drivers/staging/exfat/exfat_api.c b/drivers/staging/exfat/exfat_api.c |
| new file mode 100644 |
| index 00000000..84a0b434 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_api.c |
| @@ -0,0 +1,457 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#include <linux/version.h> |
| +#include <linux/module.h> |
| +#include <linux/init.h> |
| + |
| +#include "exfat_version.h" |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#include "exfat_data.h" |
| +#include "exfat_oal.h" |
| + |
| +#include "exfat_part.h" |
| +#include "exfat_nls.h" |
| +#include "exfat_api.h" |
| +#include "exfat_super.h" |
| +#include "exfat.h" |
| + |
| +extern FS_STRUCT_T fs_struct[]; |
| + |
| +extern struct semaphore z_sem; |
| + |
| +INT32 FsInit(void) |
| +{ |
| + INT32 i; |
| + |
| + for (i = 0; i < MAX_DRIVE; i++) { |
| + fs_struct[i].mounted = FALSE; |
| + fs_struct[i].sb = NULL; |
| + sm_init(&(fs_struct[i].v_sem)); |
| + } |
| + |
| + return(ffsInit()); |
| +} |
| + |
| +INT32 FsShutdown(void) |
| +{ |
| + INT32 i; |
| + |
| + for (i = 0; i < MAX_DRIVE; i++) { |
| + if (!fs_struct[i].mounted) continue; |
| + |
| + ffsUmountVol(fs_struct[i].sb); |
| + } |
| + |
| + return(ffsShutdown()); |
| +} |
| + |
| +INT32 FsMountVol(struct super_block *sb) |
| +{ |
| + INT32 err, drv; |
| + |
| + sm_P(&z_sem); |
| + |
| + for (drv = 0; drv < MAX_DRIVE; drv++) { |
| + if (!fs_struct[drv].mounted) break; |
| + } |
| + |
| + if (drv >= MAX_DRIVE) return(FFS_ERROR); |
| + |
| + sm_P(&(fs_struct[drv].v_sem)); |
| + |
| + err = buf_init(sb); |
| + if (!err) { |
| + err = ffsMountVol(sb, drv); |
| + } |
| + |
| + sm_V(&(fs_struct[drv].v_sem)); |
| + |
| + if (!err) { |
| + fs_struct[drv].mounted = TRUE; |
| + fs_struct[drv].sb = sb; |
| + } else { |
| + buf_shutdown(sb); |
| + } |
| + |
| + sm_V(&z_sem); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsUmountVol(struct super_block *sb) |
| +{ |
| + INT32 err; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + sm_P(&z_sem); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsUmountVol(sb); |
| + buf_shutdown(sb); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + fs_struct[p_fs->drv].mounted = FALSE; |
| + fs_struct[p_fs->drv].sb = NULL; |
| + |
| + sm_V(&z_sem); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info) |
| +{ |
| + INT32 err; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (info == NULL) return(FFS_ERROR); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsGetVolInfo(sb, info); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsSyncVol(struct super_block *sb, INT32 do_sync) |
| +{ |
| + INT32 err; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsSyncVol(sb, do_sync); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if ((fid == NULL) || (path == NULL) || (STRLEN(path) == 0)) |
| + return(FFS_ERROR); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsLookupFile(inode, path, fid); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if ((fid == NULL) || (path == NULL) || (STRLEN(path) == 0)) |
| + return(FFS_ERROR); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsCreateFile(inode, path, mode, fid); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (fid == NULL) return(FFS_INVALIDFID); |
| + |
| + if (buffer == NULL) return(FFS_ERROR); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsReadFile(inode, fid, buffer, count, rcount); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (fid == NULL) return(FFS_INVALIDFID); |
| + |
| + if (buffer == NULL) return(FFS_ERROR); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsWriteFile(inode, fid, buffer, count, wcount); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + PRINTK("FsTruncateFile entered (inode %p size %llu)\n", inode, new_size); |
| + |
| + err = ffsTruncateFile(inode, old_size, new_size); |
| + |
| + PRINTK("FsTruncateFile exitted (%d)\n", err); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = old_parent_inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (fid == NULL) return(FFS_INVALIDFID); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsMoveFile(old_parent_inode, fid, new_parent_inode, new_dentry); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsRemoveFile(struct inode *inode, FILE_ID_T *fid) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (fid == NULL) return(FFS_INVALIDFID); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsRemoveFile(inode, fid); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsSetAttr(struct inode *inode, UINT32 attr) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsSetAttr(inode, attr); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsReadStat(struct inode *inode, DIR_ENTRY_T *info) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsGetStat(inode, info); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsWriteStat(struct inode *inode, DIR_ENTRY_T *info) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + PRINTK("FsWriteStat entered (inode %p info %p\n", inode, info); |
| + |
| + err = ffsSetStat(inode, info); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + PRINTK("FsWriteStat exited (%d)\n", err); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (clu == NULL) return(FFS_ERROR); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsMapCluster(inode, clu_offset, clu); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if ((fid == NULL) || (path == NULL) || (STRLEN(path) == 0)) |
| + return(FFS_ERROR); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsCreateDir(inode, path, fid); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (dir_entry == NULL) return(FFS_ERROR); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsReadDir(inode, dir_entry); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +INT32 FsRemoveDir(struct inode *inode, FILE_ID_T *fid) |
| +{ |
| + INT32 err; |
| + struct super_block *sb = inode->i_sb; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (fid == NULL) return(FFS_INVALIDFID); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + err = ffsRemoveDir(inode, fid); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return(err); |
| +} |
| + |
| +EXPORT_SYMBOL(FsMountVol); |
| +EXPORT_SYMBOL(FsUmountVol); |
| +EXPORT_SYMBOL(FsGetVolInfo); |
| +EXPORT_SYMBOL(FsSyncVol); |
| +EXPORT_SYMBOL(FsLookupFile); |
| +EXPORT_SYMBOL(FsCreateFile); |
| +EXPORT_SYMBOL(FsReadFile); |
| +EXPORT_SYMBOL(FsWriteFile); |
| +EXPORT_SYMBOL(FsTruncateFile); |
| +EXPORT_SYMBOL(FsMoveFile); |
| +EXPORT_SYMBOL(FsRemoveFile); |
| +EXPORT_SYMBOL(FsSetAttr); |
| +EXPORT_SYMBOL(FsReadStat); |
| +EXPORT_SYMBOL(FsWriteStat); |
| +EXPORT_SYMBOL(FsMapCluster); |
| +EXPORT_SYMBOL(FsCreateDir); |
| +EXPORT_SYMBOL(FsReadDir); |
| +EXPORT_SYMBOL(FsRemoveDir); |
| + |
| +#if EXFAT_CONFIG_KERNEL_DEBUG |
| +INT32 FsReleaseCache(struct super_block *sb) |
| +{ |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + sm_P(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + FAT_release_all(sb); |
| + buf_release_all(sb); |
| + |
| + sm_V(&(fs_struct[p_fs->drv].v_sem)); |
| + |
| + return 0; |
| +} |
| + |
| +EXPORT_SYMBOL(FsReleaseCache); |
| +#endif |
| + |
| +static int __init init_exfat_core(void) |
| +{ |
| + int err; |
| + |
| + printk(KERN_INFO "exFAT: Core Version %s\n", EXFAT_VERSION); |
| + |
| + err = FsInit(); |
| + if (err) { |
| + if (err == FFS_MEMORYERR) |
| + return -ENOMEM; |
| + else |
| + return -EIO; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static void __exit exit_exfat_core(void) |
| +{ |
| + FsShutdown(); |
| +} |
| + |
| +module_init(init_exfat_core); |
| +module_exit(exit_exfat_core); |
| + |
| +MODULE_LICENSE("GPL"); |
| diff --git a/drivers/staging/exfat/exfat_api.h b/drivers/staging/exfat/exfat_api.h |
| new file mode 100644 |
| index 00000000..8de658b1 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_api.h |
| @@ -0,0 +1,165 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#ifndef _EXFAT_API_H |
| +#define _EXFAT_API_H |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| + |
| +#ifdef __cplusplus |
| +extern "C" { |
| +#endif |
| + |
| +#define EXFAT_SUPER_MAGIC (0x2011BAB0L) |
| +#define EXFAT_ROOT_INO 1 |
| + |
| +#define FAT12 0x01 |
| +#define FAT16 0x0E |
| +#define FAT32 0x0C |
| +#define EXFAT 0x07 |
| + |
| +#define MAX_CHARSET_SIZE 3 |
| +#define MAX_PATH_DEPTH 15 |
| +#define MAX_NAME_LENGTH 256 |
| +#define MAX_PATH_LENGTH 260 |
| +#define DOS_NAME_LENGTH 11 |
| +#define DOS_PATH_LENGTH 80 |
| + |
| +#define ATTR_NORMAL 0x0000 |
| +#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_SYMLINK 0x0040 |
| +#define ATTR_EXTEND 0x000F |
| +#define ATTR_RWMASK 0x007E |
| + |
| +#define FM_REGULAR 0x00 |
| +#define FM_SYMLINK 0x40 |
| + |
| +#define FFS_SUCCESS 0 |
| +#define FFS_MEDIAERR 1 |
| +#define FFS_FORMATERR 2 |
| +#define FFS_MOUNTED 3 |
| +#define FFS_NOTMOUNTED 4 |
| +#define FFS_ALIGNMENTERR 5 |
| +#define FFS_SEMAPHOREERR 6 |
| +#define FFS_INVALIDPATH 7 |
| +#define FFS_INVALIDFID 8 |
| +#define FFS_NOTFOUND 9 |
| +#define FFS_FILEEXIST 10 |
| +#define FFS_PERMISSIONERR 11 |
| +#define FFS_NOTOPENED 12 |
| +#define FFS_MAXOPENED 13 |
| +#define FFS_FULL 14 |
| +#define FFS_EOF 15 |
| +#define FFS_DIRBUSY 16 |
| +#define FFS_MEMORYERR 17 |
| +#define FFS_NAMETOOLONG 18 |
| +#define FFS_ERROR 19 |
| + |
| + typedef struct { |
| + UINT16 Year; |
| + UINT16 Month; |
| + UINT16 Day; |
| + UINT16 Hour; |
| + UINT16 Minute; |
| + UINT16 Second; |
| + UINT16 MilliSecond; |
| + } DATE_TIME_T; |
| + |
| + typedef struct { |
| + UINT32 Offset; |
| + UINT32 Size; |
| + } PART_INFO_T; |
| + |
| + typedef struct { |
| + UINT32 SecSize; |
| + UINT32 DevSize; |
| + } DEV_INFO_T; |
| + |
| + typedef struct { |
| + UINT32 FatType; |
| + UINT32 ClusterSize; |
| + UINT32 NumClusters; |
| + UINT32 FreeClusters; |
| + UINT32 UsedClusters; |
| + } VOL_INFO_T; |
| + |
| + typedef struct { |
| + UINT32 dir; |
| + INT32 size; |
| + UINT8 flags; |
| + } CHAIN_T; |
| + |
| + typedef struct { |
| + CHAIN_T dir; |
| + INT32 entry; |
| + UINT32 type; |
| + UINT32 attr; |
| + UINT32 start_clu; |
| + UINT64 size; |
| + UINT8 flags; |
| + INT64 rwoffset; |
| + INT32 hint_last_off; |
| + UINT32 hint_last_clu; |
| + } FILE_ID_T; |
| + |
| + typedef struct { |
| + INT8 Name[MAX_NAME_LENGTH *MAX_CHARSET_SIZE]; |
| + INT8 ShortName[DOS_NAME_LENGTH + 2]; |
| + UINT32 Attr; |
| + UINT64 Size; |
| + UINT32 NumSubdirs; |
| + DATE_TIME_T CreateTimestamp; |
| + DATE_TIME_T ModifyTimestamp; |
| + DATE_TIME_T AccessTimestamp; |
| + } DIR_ENTRY_T; |
| + |
| + INT32 FsInit(void); |
| + INT32 FsShutdown(void); |
| + |
| + INT32 FsMountVol(struct super_block *sb); |
| + INT32 FsUmountVol(struct super_block *sb); |
| + INT32 FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info); |
| + INT32 FsSyncVol(struct super_block *sb, INT32 do_sync); |
| + |
| + INT32 FsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid); |
| + INT32 FsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid); |
| + INT32 FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount); |
| + INT32 FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount); |
| + INT32 FsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size); |
| + INT32 FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); |
| + INT32 FsRemoveFile(struct inode *inode, FILE_ID_T *fid); |
| + INT32 FsSetAttr(struct inode *inode, UINT32 attr); |
| + INT32 FsReadStat(struct inode *inode, DIR_ENTRY_T *info); |
| + INT32 FsWriteStat(struct inode *inode, DIR_ENTRY_T *info); |
| + INT32 FsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu); |
| + |
| + INT32 FsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid); |
| + INT32 FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry); |
| + INT32 FsRemoveDir(struct inode *inode, FILE_ID_T *fid); |
| + |
| + INT32 FsReleaseCache(struct super_block *sb); |
| +#ifdef __cplusplus |
| +} |
| +#endif |
| +#endif |
| diff --git a/drivers/staging/exfat/exfat_blkdev.c b/drivers/staging/exfat/exfat_blkdev.c |
| new file mode 100644 |
| index 00000000..c5bf7a7a |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_blkdev.c |
| @@ -0,0 +1,156 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#include <linux/blkdev.h> |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#include "exfat_blkdev.h" |
| +#include "exfat_data.h" |
| +#include "exfat_api.h" |
| +#include "exfat_super.h" |
| + |
| +INT32 bdev_init(void) |
| +{ |
| + return(FFS_SUCCESS); |
| +} |
| + |
| +INT32 bdev_shutdown(void) |
| +{ |
| + return(FFS_SUCCESS); |
| +} |
| + |
| +INT32 bdev_open(struct super_block *sb) |
| +{ |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + |
| + if (p_bd->opened) return(FFS_SUCCESS); |
| + |
| + p_bd->sector_size = bdev_logical_block_size(sb->s_bdev); |
| + p_bd->sector_size_bits = my_log2(p_bd->sector_size); |
| + p_bd->sector_size_mask = p_bd->sector_size - 1; |
| + p_bd->num_sectors = i_size_read(sb->s_bdev->bd_inode) >> p_bd->sector_size_bits; |
| + |
| + p_bd->opened = TRUE; |
| + |
| + return(FFS_SUCCESS); |
| +} |
| + |
| +INT32 bdev_close(struct super_block *sb) |
| +{ |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + |
| + if (!p_bd->opened) return(FFS_SUCCESS); |
| + |
| + p_bd->opened = FALSE; |
| + return(FFS_SUCCESS); |
| +} |
| + |
| +INT32 bdev_read(struct super_block *sb, UINT32 secno, struct buffer_head **bh, UINT32 num_secs, INT32 read) |
| +{ |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| +#if EXFAT_CONFIG_KERNEL_DEBUG |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + long flags = sbi->debug_flags; |
| + |
| + if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR); |
| +#endif |
| + |
| + if (!p_bd->opened) return(FFS_MEDIAERR); |
| + |
| + if (*bh) __brelse(*bh); |
| + |
| + if (read) |
| + *bh = __bread(sb->s_bdev, secno, num_secs << p_bd->sector_size_bits); |
| + else |
| + *bh = __getblk(sb->s_bdev, secno, num_secs << p_bd->sector_size_bits); |
| + |
| + if (*bh) return(FFS_SUCCESS); |
| + |
| + WARN(!p_fs->dev_ejected, |
| + "[EXFAT] No bh, device seems wrong or to be ejected.\n"); |
| + |
| + return(FFS_MEDIAERR); |
| +} |
| + |
| +INT32 bdev_write(struct super_block *sb, UINT32 secno, struct buffer_head *bh, UINT32 num_secs, INT32 sync) |
| +{ |
| + INT32 count; |
| + struct buffer_head *bh2; |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| +#if EXFAT_CONFIG_KERNEL_DEBUG |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + long flags = sbi->debug_flags; |
| + |
| + if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR); |
| +#endif |
| + |
| + if (!p_bd->opened) return(FFS_MEDIAERR); |
| + |
| + if (secno == bh->b_blocknr) { |
| + lock_buffer(bh); |
| + set_buffer_uptodate(bh); |
| + mark_buffer_dirty(bh); |
| + unlock_buffer(bh); |
| + if (sync && (sync_dirty_buffer(bh) != 0)) |
| + return (FFS_MEDIAERR); |
| + } else { |
| + count = num_secs << p_bd->sector_size_bits; |
| + |
| + bh2 = __getblk(sb->s_bdev, secno, count); |
| + |
| + if (bh2 == NULL) |
| + goto no_bh; |
| + |
| + lock_buffer(bh2); |
| + MEMCPY(bh2->b_data, bh->b_data, count); |
| + set_buffer_uptodate(bh2); |
| + mark_buffer_dirty(bh2); |
| + unlock_buffer(bh2); |
| + if (sync && (sync_dirty_buffer(bh2) != 0)) { |
| + __brelse(bh2); |
| + goto no_bh; |
| + } |
| + __brelse(bh2); |
| + } |
| + |
| + return(FFS_SUCCESS); |
| + |
| +no_bh: |
| + WARN(!p_fs->dev_ejected, |
| + "[EXFAT] No bh, device seems wrong or to be ejected.\n"); |
| + |
| + return (FFS_MEDIAERR); |
| +} |
| + |
| +INT32 bdev_sync(struct super_block *sb) |
| +{ |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| +#if EXFAT_CONFIG_KERNEL_DEBUG |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + long flags = sbi->debug_flags; |
| + |
| + if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR); |
| +#endif |
| + |
| + if (!p_bd->opened) return(FFS_MEDIAERR); |
| + |
| + return sync_blockdev(sb->s_bdev); |
| +} |
| diff --git a/drivers/staging/exfat/exfat_blkdev.h b/drivers/staging/exfat/exfat_blkdev.h |
| new file mode 100644 |
| index 00000000..7710dd63 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_blkdev.h |
| @@ -0,0 +1,46 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#ifndef _EXFAT_BLKDEV_H |
| +#define _EXFAT_BLKDEV_H |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| + |
| +#ifdef __cplusplus |
| +extern "C" { |
| +#endif |
| + typedef struct __BD_INFO_T { |
| + INT32 sector_size; |
| + INT32 sector_size_bits; |
| + INT32 sector_size_mask; |
| + INT32 num_sectors; |
| + BOOL opened; |
| + } BD_INFO_T; |
| + |
| + INT32 bdev_init(void); |
| + INT32 bdev_shutdown(void); |
| + INT32 bdev_open(struct super_block *sb); |
| + INT32 bdev_close(struct super_block *sb); |
| + INT32 bdev_read(struct super_block *sb, UINT32 secno, struct buffer_head **bh, UINT32 num_secs, INT32 read); |
| + INT32 bdev_write(struct super_block *sb, UINT32 secno, struct buffer_head *bh, UINT32 num_secs, INT32 sync); |
| + INT32 bdev_sync(struct super_block *sb); |
| +#ifdef __cplusplus |
| +} |
| +#endif |
| +#endif |
| diff --git a/drivers/staging/exfat/exfat_cache.c b/drivers/staging/exfat/exfat_cache.c |
| new file mode 100644 |
| index 00000000..008629fb |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_cache.c |
| @@ -0,0 +1,740 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#include "exfat_data.h" |
| + |
| +#include "exfat_cache.h" |
| +#include "exfat_super.h" |
| +#include "exfat.h" |
| + |
| +extern FS_STRUCT_T fs_struct[]; |
| + |
| +#define sm_P(s) |
| +#define sm_V(s) |
| + |
| +static INT32 __FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content); |
| +static INT32 __FAT_write(struct super_block *sb, UINT32 loc, UINT32 content); |
| + |
| +static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, UINT32 sec); |
| +static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, UINT32 sec); |
| +static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); |
| +static void FAT_cache_remove_hash(BUF_CACHE_T *bp); |
| + |
| +static UINT8 *__buf_getblk(struct super_block *sb, UINT32 sec); |
| + |
| +static BUF_CACHE_T *buf_cache_find(struct super_block *sb, UINT32 sec); |
| +static BUF_CACHE_T *buf_cache_get(struct super_block *sb, UINT32 sec); |
| +static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); |
| +static void buf_cache_remove_hash(BUF_CACHE_T *bp); |
| + |
| +static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); |
| +static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); |
| +static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); |
| +static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); |
| + |
| +INT32 buf_init(struct super_block *sb) |
| +{ |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + INT32 i; |
| + |
| + p_fs->FAT_cache_lru_list.next = p_fs->FAT_cache_lru_list.prev = &p_fs->FAT_cache_lru_list; |
| + |
| + for (i = 0; i < FAT_CACHE_SIZE; i++) { |
| + p_fs->FAT_cache_array[i].drv = -1; |
| + p_fs->FAT_cache_array[i].sec = ~0; |
| + p_fs->FAT_cache_array[i].flag = 0; |
| + p_fs->FAT_cache_array[i].buf_bh = NULL; |
| + p_fs->FAT_cache_array[i].prev = p_fs->FAT_cache_array[i].next = NULL; |
| + push_to_mru(&(p_fs->FAT_cache_array[i]), &p_fs->FAT_cache_lru_list); |
| + } |
| + |
| + p_fs->buf_cache_lru_list.next = p_fs->buf_cache_lru_list.prev = &p_fs->buf_cache_lru_list; |
| + |
| + for (i = 0; i < BUF_CACHE_SIZE; i++) { |
| + p_fs->buf_cache_array[i].drv = -1; |
| + p_fs->buf_cache_array[i].sec = ~0; |
| + p_fs->buf_cache_array[i].flag = 0; |
| + p_fs->buf_cache_array[i].buf_bh = NULL; |
| + p_fs->buf_cache_array[i].prev = p_fs->buf_cache_array[i].next = NULL; |
| + push_to_mru(&(p_fs->buf_cache_array[i]), &p_fs->buf_cache_lru_list); |
| + } |
| + |
| + for (i = 0; i < FAT_CACHE_HASH_SIZE; i++) { |
| + p_fs->FAT_cache_hash_list[i].drv = -1; |
| + p_fs->FAT_cache_hash_list[i].sec = ~0; |
| + p_fs->FAT_cache_hash_list[i].hash_next = p_fs->FAT_cache_hash_list[i].hash_prev = &(p_fs->FAT_cache_hash_list[i]); |
| + } |
| + |
| + for (i = 0; i < FAT_CACHE_SIZE; i++) { |
| + FAT_cache_insert_hash(sb, &(p_fs->FAT_cache_array[i])); |
| + } |
| + |
| + for (i = 0; i < BUF_CACHE_HASH_SIZE; i++) { |
| + p_fs->buf_cache_hash_list[i].drv = -1; |
| + p_fs->buf_cache_hash_list[i].sec = ~0; |
| + p_fs->buf_cache_hash_list[i].hash_next = p_fs->buf_cache_hash_list[i].hash_prev = &(p_fs->buf_cache_hash_list[i]); |
| + } |
| + |
| + for (i = 0; i < BUF_CACHE_SIZE; i++) { |
| + buf_cache_insert_hash(sb, &(p_fs->buf_cache_array[i])); |
| + } |
| + |
| + return(FFS_SUCCESS); |
| +} |
| + |
| +INT32 buf_shutdown(struct super_block *sb) |
| +{ |
| + return(FFS_SUCCESS); |
| +} |
| + |
| +INT32 FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content) |
| +{ |
| + INT32 ret; |
| + |
| + sm_P(&f_sem); |
| + |
| + ret = __FAT_read(sb, loc, content); |
| + |
| + sm_V(&f_sem); |
| + |
| + return(ret); |
| +} |
| + |
| +INT32 FAT_write(struct super_block *sb, UINT32 loc, UINT32 content) |
| +{ |
| + INT32 ret; |
| + |
| + sm_P(&f_sem); |
| + |
| + ret = __FAT_write(sb, loc, content); |
| + |
| + sm_V(&f_sem); |
| + |
| + return(ret); |
| +} |
| + |
| +static INT32 __FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content) |
| +{ |
| + INT32 off; |
| + UINT32 sec, _content; |
| + UINT8 *fat_sector, *fat_entry; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + |
| + if (p_fs->vol_type == FAT12) { |
| + sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); |
| + off = (loc + (loc >> 1)) & p_bd->sector_size_mask; |
| + |
| + if (off == (p_bd->sector_size-1)) { |
| + fat_sector = FAT_getblk(sb, sec); |
| + if (!fat_sector) |
| + return -1; |
| + |
| + _content = (UINT32) fat_sector[off]; |
| + |
| + fat_sector = FAT_getblk(sb, ++sec); |
| + if (!fat_sector) |
| + return -1; |
| + |
| + _content |= (UINT32) fat_sector[0] << 8; |
| + } else { |
| + fat_sector = FAT_getblk(sb, sec); |
| + if (!fat_sector) |
| + return -1; |
| + |
| + fat_entry = &(fat_sector[off]); |
| + _content = GET16(fat_entry); |
| + } |
| + |
| + if (loc & 1) _content >>= 4; |
| + |
| + _content &= 0x00000FFF; |
| + |
| + if (_content >= CLUSTER_16(0x0FF8)) { |
| + *content = CLUSTER_32(~0); |
| + return 0; |
| + } else { |
| + *content = CLUSTER_32(_content); |
| + return 0; |
| + } |
| + } else if (p_fs->vol_type == FAT16) { |
| + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); |
| + off = (loc << 1) & p_bd->sector_size_mask; |
| + |
| + fat_sector = FAT_getblk(sb, sec); |
| + if |
| + (!fat_sector) |
| + return -1; |
| + |
| + fat_entry = &(fat_sector[off]); |
| + |
| + _content = GET16_A(fat_entry); |
| + |
| + _content &= 0x0000FFFF; |
| + |
| + if (_content >= CLUSTER_16(0xFFF8)) { |
| + *content = CLUSTER_32(~0); |
| + return 0; |
| + } else { |
| + *content = CLUSTER_32(_content); |
| + return 0; |
| + } |
| + } else if (p_fs->vol_type == FAT32) { |
| + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); |
| + off = (loc << 2) & p_bd->sector_size_mask; |
| + |
| + fat_sector = FAT_getblk(sb, sec); |
| + if (!fat_sector) |
| + return -1; |
| + |
| + fat_entry = &(fat_sector[off]); |
| + |
| + _content = GET32_A(fat_entry); |
| + |
| + _content &= 0x0FFFFFFF; |
| + |
| + if (_content >= CLUSTER_32(0x0FFFFFF8)) { |
| + *content = CLUSTER_32(~0); |
| + return 0; |
| + } else { |
| + *content = CLUSTER_32(_content); |
| + return 0; |
| + } |
| + } else { |
| + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); |
| + off = (loc << 2) & p_bd->sector_size_mask; |
| + |
| + fat_sector = FAT_getblk(sb, sec); |
| + if (!fat_sector) |
| + return -1; |
| + |
| + fat_entry = &(fat_sector[off]); |
| + _content = GET32_A(fat_entry); |
| + |
| + if (_content >= CLUSTER_32(0xFFFFFFF8)) { |
| + *content = CLUSTER_32(~0); |
| + return 0; |
| + } else { |
| + *content = CLUSTER_32(_content); |
| + return 0; |
| + } |
| + } |
| + |
| + *content = CLUSTER_32(~0); |
| + return 0; |
| +} |
| + |
| +static INT32 __FAT_write(struct super_block *sb, UINT32 loc, UINT32 content) |
| +{ |
| + INT32 off; |
| + UINT32 sec; |
| + UINT8 *fat_sector, *fat_entry; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + |
| + if (p_fs->vol_type == FAT12) { |
| + |
| + content &= 0x00000FFF; |
| + |
| + sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); |
| + off = (loc + (loc >> 1)) & p_bd->sector_size_mask; |
| + |
| + fat_sector = FAT_getblk(sb, sec); |
| + if (!fat_sector) |
| + return -1; |
| + |
| + if (loc & 1) { |
| + |
| + content <<= 4; |
| + |
| + if (off == (p_bd->sector_size-1)) { |
| + fat_sector[off] = (UINT8)(content | (fat_sector[off] & 0x0F)); |
| + FAT_modify(sb, sec); |
| + |
| + fat_sector = FAT_getblk(sb, ++sec); |
| + if (!fat_sector) |
| + return -1; |
| + |
| + fat_sector[0] = (UINT8)(content >> 8); |
| + } else { |
| + fat_entry = &(fat_sector[off]); |
| + content |= GET16(fat_entry) & 0x000F; |
| + |
| + SET16(fat_entry, content); |
| + } |
| + } else { |
| + fat_sector[off] = (UINT8)(content); |
| + |
| + if (off == (p_bd->sector_size-1)) { |
| + fat_sector[off] = (UINT8)(content); |
| + FAT_modify(sb, sec); |
| + |
| + fat_sector = FAT_getblk(sb, ++sec); |
| + fat_sector[0] = (UINT8)((fat_sector[0] & 0xF0) | (content >> 8)); |
| + } else { |
| + fat_entry = &(fat_sector[off]); |
| + content |= GET16(fat_entry) & 0xF000; |
| + |
| + SET16(fat_entry, content); |
| + } |
| + } |
| + } |
| + |
| + else if (p_fs->vol_type == FAT16) { |
| + |
| + content &= 0x0000FFFF; |
| + |
| + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); |
| + off = (loc << 1) & p_bd->sector_size_mask; |
| + |
| + fat_sector = FAT_getblk(sb, sec); |
| + if (!fat_sector) |
| + return -1; |
| + |
| + fat_entry = &(fat_sector[off]); |
| + |
| + SET16_A(fat_entry, content); |
| + } |
| + |
| + else if (p_fs->vol_type == FAT32) { |
| + |
| + content &= 0x0FFFFFFF; |
| + |
| + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); |
| + off = (loc << 2) & p_bd->sector_size_mask; |
| + |
| + fat_sector = FAT_getblk(sb, sec); |
| + if (!fat_sector) |
| + return -1; |
| + |
| + fat_entry = &(fat_sector[off]); |
| + |
| + content |= GET32_A(fat_entry) & 0xF0000000; |
| + |
| + SET32_A(fat_entry, content); |
| + } |
| + |
| + else { |
| + |
| + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); |
| + off = (loc << 2) & p_bd->sector_size_mask; |
| + |
| + fat_sector = FAT_getblk(sb, sec); |
| + if (!fat_sector) |
| + return -1; |
| + |
| + fat_entry = &(fat_sector[off]); |
| + |
| + SET32_A(fat_entry, content); |
| + } |
| + |
| + FAT_modify(sb, sec); |
| + return 0; |
| +} |
| + |
| +UINT8 *FAT_getblk(struct super_block *sb, UINT32 sec) |
| +{ |
| + BUF_CACHE_T *bp; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + bp = FAT_cache_find(sb, sec); |
| + if (bp != NULL) { |
| + move_to_mru(bp, &p_fs->FAT_cache_lru_list); |
| + return(bp->buf_bh->b_data); |
| + } |
| + |
| + bp = FAT_cache_get(sb, sec); |
| + |
| + FAT_cache_remove_hash(bp); |
| + |
| + bp->drv = p_fs->drv; |
| + bp->sec = sec; |
| + bp->flag = 0; |
| + |
| + FAT_cache_insert_hash(sb, bp); |
| + |
| + if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { |
| + FAT_cache_remove_hash(bp); |
| + bp->drv = -1; |
| + bp->sec = ~0; |
| + bp->flag = 0; |
| + bp->buf_bh = NULL; |
| + |
| + move_to_lru(bp, &p_fs->FAT_cache_lru_list); |
| + return NULL; |
| + } |
| + |
| + return(bp->buf_bh->b_data); |
| +} |
| + |
| +void FAT_modify(struct super_block *sb, UINT32 sec) |
| +{ |
| + BUF_CACHE_T *bp; |
| + |
| + bp = FAT_cache_find(sb, sec); |
| + if (bp != NULL) { |
| + sector_write(sb, sec, bp->buf_bh, 0); |
| + } |
| +} |
| + |
| +void FAT_release_all(struct super_block *sb) |
| +{ |
| + BUF_CACHE_T *bp; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + sm_P(&f_sem); |
| + |
| + bp = p_fs->FAT_cache_lru_list.next; |
| + while (bp != &p_fs->FAT_cache_lru_list) { |
| + if (bp->drv == p_fs->drv) { |
| + bp->drv = -1; |
| + bp->sec = ~0; |
| + bp->flag = 0; |
| + |
| + if(bp->buf_bh) { |
| + __brelse(bp->buf_bh); |
| + bp->buf_bh = NULL; |
| + } |
| + } |
| + bp = bp->next; |
| + } |
| + |
| + sm_V(&f_sem); |
| +} |
| + |
| +void FAT_sync(struct super_block *sb) |
| +{ |
| + BUF_CACHE_T *bp; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + sm_P(&f_sem); |
| + |
| + bp = p_fs->FAT_cache_lru_list.next; |
| + while (bp != &p_fs->FAT_cache_lru_list) { |
| + if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { |
| + sync_dirty_buffer(bp->buf_bh); |
| + bp->flag &= ~(DIRTYBIT); |
| + } |
| + bp = bp->next; |
| + } |
| + |
| + sm_V(&f_sem); |
| +} |
| + |
| +static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, UINT32 sec) |
| +{ |
| + INT32 off; |
| + BUF_CACHE_T *bp, *hp; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE - 1); |
| + |
| + hp = &(p_fs->FAT_cache_hash_list[off]); |
| + for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { |
| + if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { |
| + |
| + WARN(!bp->buf_bh, "[EXFAT] FAT_cache has no bh. " |
| + "It will make system panic.\n"); |
| + |
| + touch_buffer(bp->buf_bh); |
| + return(bp); |
| + } |
| + } |
| + return(NULL); |
| +} |
| + |
| +static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, UINT32 sec) |
| +{ |
| + BUF_CACHE_T *bp; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + bp = p_fs->FAT_cache_lru_list.prev; |
| + |
| + |
| + move_to_mru(bp, &p_fs->FAT_cache_lru_list); |
| + return(bp); |
| +} |
| + |
| +static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) |
| +{ |
| + INT32 off; |
| + BUF_CACHE_T *hp; |
| + FS_INFO_T *p_fs; |
| + |
| + p_fs = &(EXFAT_SB(sb)->fs_info); |
| + off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE-1); |
| + |
| + hp = &(p_fs->FAT_cache_hash_list[off]); |
| + bp->hash_next = hp->hash_next; |
| + bp->hash_prev = hp; |
| + hp->hash_next->hash_prev = bp; |
| + hp->hash_next = bp; |
| +} |
| + |
| +static void FAT_cache_remove_hash(BUF_CACHE_T *bp) |
| +{ |
| + (bp->hash_prev)->hash_next = bp->hash_next; |
| + (bp->hash_next)->hash_prev = bp->hash_prev; |
| +} |
| + |
| +UINT8 *buf_getblk(struct super_block *sb, UINT32 sec) |
| +{ |
| + UINT8 *buf; |
| + |
| + sm_P(&b_sem); |
| + |
| + buf = __buf_getblk(sb, sec); |
| + |
| + sm_V(&b_sem); |
| + |
| + return(buf); |
| +} |
| + |
| +static UINT8 *__buf_getblk(struct super_block *sb, UINT32 sec) |
| +{ |
| + BUF_CACHE_T *bp; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + bp = buf_cache_find(sb, sec); |
| + if (bp != NULL) { |
| + move_to_mru(bp, &p_fs->buf_cache_lru_list); |
| + return(bp->buf_bh->b_data); |
| + } |
| + |
| + bp = buf_cache_get(sb, sec); |
| + |
| + buf_cache_remove_hash(bp); |
| + |
| + bp->drv = p_fs->drv; |
| + bp->sec = sec; |
| + bp->flag = 0; |
| + |
| + buf_cache_insert_hash(sb, bp); |
| + |
| + if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { |
| + buf_cache_remove_hash(bp); |
| + bp->drv = -1; |
| + bp->sec = ~0; |
| + bp->flag = 0; |
| + bp->buf_bh = NULL; |
| + |
| + move_to_lru(bp, &p_fs->buf_cache_lru_list); |
| + return NULL; |
| + } |
| + |
| + return(bp->buf_bh->b_data); |
| + |
| +} |
| + |
| +void buf_modify(struct super_block *sb, UINT32 sec) |
| +{ |
| + BUF_CACHE_T *bp; |
| + |
| + sm_P(&b_sem); |
| + |
| + bp = buf_cache_find(sb, sec); |
| + if (likely(bp != NULL)) { |
| + sector_write(sb, sec, bp->buf_bh, 0); |
| + } |
| + |
| + WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); |
| + |
| + sm_V(&b_sem); |
| +} |
| + |
| +void buf_lock(struct super_block *sb, UINT32 sec) |
| +{ |
| + BUF_CACHE_T *bp; |
| + |
| + sm_P(&b_sem); |
| + |
| + bp = buf_cache_find(sb, sec); |
| + if (likely(bp != NULL)) bp->flag |= LOCKBIT; |
| + |
| + WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); |
| + |
| + sm_V(&b_sem); |
| +} |
| + |
| +void buf_unlock(struct super_block *sb, UINT32 sec) |
| +{ |
| + BUF_CACHE_T *bp; |
| + |
| + sm_P(&b_sem); |
| + |
| + bp = buf_cache_find(sb, sec); |
| + if (likely(bp != NULL)) bp->flag &= ~(LOCKBIT); |
| + |
| + WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); |
| + |
| + sm_V(&b_sem); |
| +} |
| + |
| +void buf_release(struct super_block *sb, UINT32 sec) |
| +{ |
| + BUF_CACHE_T *bp; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + sm_P(&b_sem); |
| + |
| + bp = buf_cache_find(sb, sec); |
| + if (likely(bp != NULL)) { |
| + bp->drv = -1; |
| + bp->sec = ~0; |
| + bp->flag = 0; |
| + |
| + if(bp->buf_bh) { |
| + __brelse(bp->buf_bh); |
| + bp->buf_bh = NULL; |
| + } |
| + |
| + move_to_lru(bp, &p_fs->buf_cache_lru_list); |
| + } |
| + |
| + sm_V(&b_sem); |
| +} |
| + |
| +void buf_release_all(struct super_block *sb) |
| +{ |
| + BUF_CACHE_T *bp; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + sm_P(&b_sem); |
| + |
| + bp = p_fs->buf_cache_lru_list.next; |
| + while (bp != &p_fs->buf_cache_lru_list) { |
| + if (bp->drv == p_fs->drv) { |
| + bp->drv = -1; |
| + bp->sec = ~0; |
| + bp->flag = 0; |
| + |
| + if(bp->buf_bh) { |
| + __brelse(bp->buf_bh); |
| + bp->buf_bh = NULL; |
| + } |
| + } |
| + bp = bp->next; |
| + } |
| + |
| + sm_V(&b_sem); |
| +} |
| + |
| +void buf_sync(struct super_block *sb) |
| +{ |
| + BUF_CACHE_T *bp; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + sm_P(&b_sem); |
| + |
| + bp = p_fs->buf_cache_lru_list.next; |
| + while (bp != &p_fs->buf_cache_lru_list) { |
| + if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { |
| + sync_dirty_buffer(bp->buf_bh); |
| + bp->flag &= ~(DIRTYBIT); |
| + } |
| + bp = bp->next; |
| + } |
| + |
| + sm_V(&b_sem); |
| +} |
| + |
| +static BUF_CACHE_T *buf_cache_find(struct super_block *sb, UINT32 sec) |
| +{ |
| + INT32 off; |
| + BUF_CACHE_T *bp, *hp; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE - 1); |
| + |
| + hp = &(p_fs->buf_cache_hash_list[off]); |
| + for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { |
| + if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { |
| + touch_buffer(bp->buf_bh); |
| + return(bp); |
| + } |
| + } |
| + return(NULL); |
| +} |
| + |
| +static BUF_CACHE_T *buf_cache_get(struct super_block *sb, UINT32 sec) |
| +{ |
| + BUF_CACHE_T *bp; |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + bp = p_fs->buf_cache_lru_list.prev; |
| + while (bp->flag & LOCKBIT) bp = bp->prev; |
| + |
| + |
| + move_to_mru(bp, &p_fs->buf_cache_lru_list); |
| + return(bp); |
| +} |
| + |
| +static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) |
| +{ |
| + INT32 off; |
| + BUF_CACHE_T *hp; |
| + FS_INFO_T *p_fs; |
| + |
| + p_fs = &(EXFAT_SB(sb)->fs_info); |
| + off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE-1); |
| + |
| + hp = &(p_fs->buf_cache_hash_list[off]); |
| + bp->hash_next = hp->hash_next; |
| + bp->hash_prev = hp; |
| + hp->hash_next->hash_prev = bp; |
| + hp->hash_next = bp; |
| +} |
| + |
| +static void buf_cache_remove_hash(BUF_CACHE_T *bp) |
| +{ |
| + (bp->hash_prev)->hash_next = bp->hash_next; |
| + (bp->hash_next)->hash_prev = bp->hash_prev; |
| +} |
| + |
| +static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list) |
| +{ |
| + bp->next = list->next; |
| + bp->prev = list; |
| + list->next->prev = bp; |
| + list->next = bp; |
| +} |
| + |
| +static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list) |
| +{ |
| + bp->prev = list->prev; |
| + bp->next = list; |
| + list->prev->next = bp; |
| + list->prev = bp; |
| +} |
| + |
| +static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list) |
| +{ |
| + bp->prev->next = bp->next; |
| + bp->next->prev = bp->prev; |
| + push_to_mru(bp, list); |
| +} |
| + |
| +static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list) |
| +{ |
| + bp->prev->next = bp->next; |
| + bp->next->prev = bp->prev; |
| + push_to_lru(bp, list); |
| +} |
| diff --git a/drivers/staging/exfat/exfat_cache.h b/drivers/staging/exfat/exfat_cache.h |
| new file mode 100644 |
| index 00000000..76745095 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_cache.h |
| @@ -0,0 +1,63 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#ifndef _EXFAT_CACHE_H |
| +#define _EXFAT_CACHE_H |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| + |
| +#ifdef __cplusplus |
| +extern "C" { |
| +#endif |
| + |
| +#define LOCKBIT 0x01 |
| +#define DIRTYBIT 0x02 |
| + |
| + typedef struct __BUF_CACHE_T { |
| + struct __BUF_CACHE_T *next; |
| + struct __BUF_CACHE_T *prev; |
| + struct __BUF_CACHE_T *hash_next; |
| + struct __BUF_CACHE_T *hash_prev; |
| + INT32 drv; |
| + UINT32 sec; |
| + UINT32 flag; |
| + struct buffer_head *buf_bh; |
| + } BUF_CACHE_T; |
| + |
| + INT32 buf_init(struct super_block *sb); |
| + INT32 buf_shutdown(struct super_block *sb); |
| + INT32 FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content); |
| + INT32 FAT_write(struct super_block *sb, UINT32 loc, UINT32 content); |
| + UINT8 *FAT_getblk(struct super_block *sb, UINT32 sec); |
| + void FAT_modify(struct super_block *sb, UINT32 sec); |
| + void FAT_release_all(struct super_block *sb); |
| + void FAT_sync(struct super_block *sb); |
| + UINT8 *buf_getblk(struct super_block *sb, UINT32 sec); |
| + void buf_modify(struct super_block *sb, UINT32 sec); |
| + void buf_lock(struct super_block *sb, UINT32 sec); |
| + void buf_unlock(struct super_block *sb, UINT32 sec); |
| + void buf_release(struct super_block *sb, UINT32 sec); |
| + void buf_release_all(struct super_block *sb); |
| + void buf_sync(struct super_block *sb); |
| + |
| +#ifdef __cplusplus |
| +} |
| +#endif |
| + |
| +#endif |
| diff --git a/drivers/staging/exfat/exfat_config.h b/drivers/staging/exfat/exfat_config.h |
| new file mode 100644 |
| index 00000000..b3cd0112 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_config.h |
| @@ -0,0 +1,57 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#ifndef _EXFAT_CONFIG_H |
| +#define _EXFAT_CONFIG_H |
| + |
| +#ifdef __cplusplus |
| +extern "C" { |
| +#endif |
| + |
| +#define OS_NONOS 1 |
| +#define OS_LINUX 2 |
| + |
| +#define FFS_CONFIG_OS OS_LINUX |
| + |
| +#define FFS_CONFIG_LITTLE_ENDIAN 1 |
| +#define FFS_CONFIG_LEGACY_32BIT_API 0 |
| +#define FFS_CONFIG_LEGACY_32BIT_API 0 |
| +#define FFS_CONFIG_SUPPORT_CP1250 1 |
| +#define FFS_CONFIG_SUPPORT_CP1251 1 |
| +#define FFS_CONFIG_SUPPORT_CP1252 1 |
| +#define FFS_CONFIG_SUPPORT_CP1253 1 |
| +#define FFS_CONFIG_SUPPORT_CP1254 1 |
| +#define FFS_CONFIG_SUPPORT_CP1255 1 |
| +#define FFS_CONFIG_SUPPORT_CP1256 1 |
| +#define FFS_CONFIG_SUPPORT_CP1257 1 |
| +#define FFS_CONFIG_SUPPORT_CP1258 1 |
| +#define FFS_CONFIG_SUPPORT_CP874 1 |
| +#define FFS_CONFIG_SUPPORT_CP932 1 |
| +#define FFS_CONFIG_SUPPORT_CP936 1 |
| +#define FFS_CONFIG_SUPPORT_CP949 1 |
| +#define FFS_CONFIG_SUPPORT_CP950 1 |
| +#define FFS_CONFIG_SUPPORT_UTF8 1 |
| +#define EXFAT_CONFIG_DISCARD 1 |
| +#define EXFAT_CONFIG_KERNEL_DEBUG 1 |
| +#define EXFAT_CONFIG_DEBUG_MSG 0 |
| + |
| +#ifdef __cplusplus |
| +} |
| +#endif |
| + |
| +#endif |
| diff --git a/drivers/staging/exfat/exfat_data.c b/drivers/staging/exfat/exfat_data.c |
| new file mode 100644 |
| index 00000000..6c58eac8 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_data.c |
| @@ -0,0 +1,40 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#include "exfat_data.h" |
| +#include "exfat_oal.h" |
| + |
| +#include "exfat_blkdev.h" |
| +#include "exfat_cache.h" |
| +#include "exfat_nls.h" |
| +#include "exfat_super.h" |
| +#include "exfat.h" |
| + |
| +FS_STRUCT_T fs_struct[MAX_DRIVE]; |
| + |
| +DECLARE_MUTEX(f_sem); |
| +BUF_CACHE_T FAT_cache_array[FAT_CACHE_SIZE]; |
| +BUF_CACHE_T FAT_cache_lru_list; |
| +BUF_CACHE_T FAT_cache_hash_list[FAT_CACHE_HASH_SIZE]; |
| + |
| +DECLARE_MUTEX(b_sem); |
| +BUF_CACHE_T buf_cache_array[BUF_CACHE_SIZE]; |
| +BUF_CACHE_T buf_cache_lru_list; |
| +BUF_CACHE_T buf_cache_hash_list[BUF_CACHE_HASH_SIZE]; |
| diff --git a/drivers/staging/exfat/exfat_data.h b/drivers/staging/exfat/exfat_data.h |
| new file mode 100644 |
| index 00000000..27ca835c |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_data.h |
| @@ -0,0 +1,39 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#ifndef _EXFAT_DATA_H |
| +#define _EXFAT_DATA_H |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#ifdef __cplusplus |
| +extern "C" { |
| +#endif |
| +#define MAX_DEVICE 2 |
| +#define MAX_DRIVE 2 |
| +#define MAX_OPEN 20 |
| +#define MAX_DENTRY 512 |
| +#define FAT_CACHE_SIZE 128 |
| +#define FAT_CACHE_HASH_SIZE 64 |
| +#define BUF_CACHE_SIZE 256 |
| +#define BUF_CACHE_HASH_SIZE 64 |
| +#define DEFAULT_CODEPAGE 437 |
| +#define DEFAULT_IOCHARSET "utf8" |
| +#ifdef __cplusplus |
| +} |
| +#endif |
| +#endif |
| diff --git a/drivers/staging/exfat/exfat_global.c b/drivers/staging/exfat/exfat_global.c |
| new file mode 100644 |
| index 00000000..89934f9d |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_global.c |
| @@ -0,0 +1,119 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| + |
| +INT32 __wstrchr(UINT16 *str, UINT16 wchar) |
| +{ |
| + while (*str) { |
| + if (*(str++) == wchar) return(1); |
| + } |
| + return(0); |
| +} |
| + |
| +INT32 __wstrlen(UINT16 *str) |
| +{ |
| + INT32 length = 0; |
| + |
| + while (*(str++)) length++; |
| + return(length); |
| +} |
| + |
| +#define BITMAP_LOC(v) ((v) >> 3) |
| +#define BITMAP_SHIFT(v) ((v) & 0x07) |
| + |
| +void Bitmap_set_all(UINT8 *bitmap, INT32 mapsize) |
| +{ |
| + MEMSET(bitmap, 0xFF, mapsize); |
| +} |
| + |
| +void Bitmap_clear_all(UINT8 *bitmap, INT32 mapsize) |
| +{ |
| + MEMSET(bitmap, 0x0, mapsize); |
| +} |
| + |
| +INT32 Bitmap_test(UINT8 *bitmap, INT32 i) |
| +{ |
| + UINT8 data; |
| + |
| + data = bitmap[BITMAP_LOC(i)]; |
| + if ((data >> BITMAP_SHIFT(i)) & 0x01) return(1); |
| + return(0); |
| +} |
| + |
| +void Bitmap_set(UINT8 *bitmap, INT32 i) |
| +{ |
| + bitmap[BITMAP_LOC(i)] |= (0x01 << BITMAP_SHIFT(i)); |
| +} |
| + |
| +void Bitmap_clear(UINT8 *bitmap, INT32 i) |
| +{ |
| + bitmap[BITMAP_LOC(i)] &= ~(0x01 << BITMAP_SHIFT(i)); |
| +} |
| + |
| +void Bitmap_nbits_set(UINT8 *bitmap, INT32 offset, INT32 nbits) |
| +{ |
| + INT32 i; |
| + |
| + for (i = 0; i < nbits; i++) { |
| + Bitmap_set(bitmap, offset+i); |
| + } |
| +} |
| + |
| +void Bitmap_nbits_clear(UINT8 *bitmap, INT32 offset, INT32 nbits) |
| +{ |
| + INT32 i; |
| + |
| + for (i = 0; i < nbits; i++) { |
| + Bitmap_clear(bitmap, offset+i); |
| + } |
| +} |
| + |
| +void my_itoa(INT8 *buf, INT32 v) |
| +{ |
| + INT32 mod[10]; |
| + INT32 i; |
| + |
| + for (i = 0; i < 10; i++) { |
| + mod[i] = (v % 10); |
| + v = v / 10; |
| + if (v == 0) break; |
| + } |
| + |
| + if (i == 10) |
| + i--; |
| + |
| + for (; i >= 0; i--) { |
| + *buf = (UINT8) ('0' + mod[i]); |
| + buf++; |
| + } |
| + *buf = '\0'; |
| +} |
| + |
| +INT32 my_log2(UINT32 v) |
| +{ |
| + UINT32 bits = 0; |
| + |
| + while (v > 1) { |
| + if (v & 0x01) return(-1); |
| + v >>= 1; |
| + bits++; |
| + } |
| + return(bits); |
| +} |
| diff --git a/drivers/staging/exfat/exfat_global.h b/drivers/staging/exfat/exfat_global.h |
| new file mode 100644 |
| index 00000000..9d87deb2 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_global.h |
| @@ -0,0 +1,127 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#ifndef _EXFAT_GLOBAL_H |
| +#define _EXFAT_GLOBAL_H |
| + |
| +#include <linux/kernel.h> |
| +#include <linux/mm.h> |
| +#include <linux/slab.h> |
| +#include <linux/string.h> |
| +#include <linux/fs.h> |
| + |
| +#include "exfat_config.h" |
| + |
| +#ifdef __cplusplus |
| +extern "C" { |
| +#endif |
| + |
| +#ifndef TRUE |
| +#define TRUE 1 |
| +#endif |
| +#ifndef FALSE |
| +#define FALSE 0 |
| +#endif |
| +#ifndef OK |
| +#define OK 0 |
| +#endif |
| +#ifndef FAIL |
| +#define FAIL 1 |
| +#endif |
| +#ifndef NULL |
| +#define NULL 0 |
| +#endif |
| + |
| +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) |
| +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) |
| + |
| + typedef char INT8; |
| + typedef short INT16; |
| + typedef int INT32; |
| + typedef long long INT64; |
| + typedef unsigned char UINT8; |
| + typedef unsigned short UINT16; |
| + typedef unsigned int UINT32; |
| + typedef unsigned long long UINT64; |
| + typedef unsigned char BOOL; |
| + |
| +#ifdef MALLOC |
| +#undef MALLOC |
| +#endif |
| +#ifdef FREE |
| +#undef FREE |
| +#endif |
| +#ifdef MEMSET |
| +#undef MEMSET |
| +#endif |
| +#ifdef MEMCPY |
| +#undef MEMCPY |
| +#endif |
| +#ifdef MEMCMP |
| +#undef MEMCMP |
| +#endif |
| + |
| +#define MALLOC(size) kmalloc(size, GFP_KERNEL) |
| +#define FREE(mem) if (mem) kfree(mem) |
| +#define MEMSET(mem, value, size) memset(mem, value, size) |
| +#define MEMCPY(dest, src, size) memcpy(dest, src, size) |
| +#define MEMCMP(mem1, mem2, size) memcmp(mem1, mem2, size) |
| +#define COPY_DENTRY(dest, src) memcpy(dest, src, sizeof(DENTRY_T)) |
| + |
| +#define STRCPY(dest, src) strcpy(dest, src) |
| +#define STRNCPY(dest, src, n) strncpy(dest, src, n) |
| +#define STRCAT(str1, str2) strcat(str1, str2) |
| +#define STRCMP(str1, str2) strcmp(str1, str2) |
| +#define STRNCMP(str1, str2, n) strncmp(str1, str2, n) |
| +#define STRLEN(str) strlen(str) |
| + |
| + INT32 __wstrchr(UINT16 *str, UINT16 wchar); |
| + INT32 __wstrlen(UINT16 *str); |
| + |
| +#define WSTRCHR(str, wchar) __wstrchr(str, wchar) |
| +#define WSTRLEN(str) __wstrlen(str) |
| + |
| +#if EXFAT_CONFIG_DEBUG_MSG |
| +#define PRINTK(...) \ |
| + do { \ |
| + printk("[EXFAT] " __VA_ARGS__); \ |
| + } while(0) |
| +#else |
| +#define PRINTK(...) |
| +#endif |
| + |
| + void Bitmap_set_all(UINT8 *bitmap, INT32 mapsize); |
| + void Bitmap_clear_all(UINT8 *bitmap, INT32 mapsize); |
| + INT32 Bitmap_test(UINT8 *bitmap, INT32 i); |
| + void Bitmap_set(UINT8 *bitmap, INT32 i); |
| + void Bitmap_clear(UINT8 *bitmpa, INT32 i); |
| + void Bitmap_nbits_set(UINT8 *bitmap, INT32 offset, INT32 nbits); |
| + void Bitmap_nbits_clear(UINT8 *bitmap, INT32 offset, INT32 nbits); |
| + |
| + void my_itoa(INT8 *buf, INT32 v); |
| + INT32 my_log2(UINT32 v); |
| + |
| +#ifdef PRINT |
| +#undef PRINT |
| +#endif |
| + |
| +#define PRINT printk |
| +#ifdef __cplusplus |
| +} |
| +#endif |
| +#endif |
| diff --git a/drivers/staging/exfat/exfat_nls.c b/drivers/staging/exfat/exfat_nls.c |
| new file mode 100644 |
| index 00000000..04ac3998 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_nls.c |
| @@ -0,0 +1,347 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#include "exfat_data.h" |
| + |
| +#include "exfat_nls.h" |
| +#include "exfat_api.h" |
| +#include "exfat_super.h" |
| +#include "exfat.h" |
| + |
| +#include <linux/nls.h> |
| + |
| +static UINT16 bad_dos_chars[] = { |
| + 0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D, |
| + 0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D, |
| + 0 |
| +}; |
| + |
| +static UINT16 bad_uni_chars[] = { |
| + 0x0022, 0x002A, 0x002F, 0x003A, |
| + 0x003C, 0x003E, 0x003F, 0x005C, 0x007C, |
| + 0 |
| +}; |
| + |
| +static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy); |
| +static INT32 convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy); |
| + |
| +UINT16 nls_upper(struct super_block *sb, UINT16 a) |
| +{ |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + |
| + if (EXFAT_SB(sb)->options.casesensitive) |
| + return(a); |
| + if ((p_fs->vol_utbl)[get_col_index(a)] != NULL) |
| + return (p_fs->vol_utbl)[get_col_index(a)][get_row_index(a)]; |
| + else |
| + return a; |
| +} |
| + |
| +INT32 nls_dosname_cmp(struct super_block *sb, UINT8 *a, UINT8 *b) |
| +{ |
| + return(STRNCMP((void *) a, (void *) b, DOS_NAME_LENGTH)); |
| +} |
| + |
| +INT32 nls_uniname_cmp(struct super_block *sb, UINT16 *a, UINT16 *b) |
| +{ |
| + INT32 i; |
| + |
| + for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) { |
| + if (nls_upper(sb, *a) != nls_upper(sb, *b)) return(1); |
| + if (*a == 0x0) return(0); |
| + } |
| + return(0); |
| +} |
| + |
| +void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, INT32 *p_lossy) |
| +{ |
| + INT32 i, j, len, lossy = FALSE; |
| + UINT8 buf[MAX_CHARSET_SIZE]; |
| + UINT8 lower = 0, upper = 0; |
| + UINT8 *dosname = p_dosname->name; |
| + UINT16 *uniname = p_uniname->name; |
| + UINT16 *p, *last_period; |
| + struct nls_table *nls = EXFAT_SB(sb)->nls_disk; |
| + |
| + for (i = 0; i < DOS_NAME_LENGTH; i++) { |
| + *(dosname+i) = ' '; |
| + } |
| + |
| + if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_CUR_DIR_NAME)) { |
| + *(dosname) = '.'; |
| + p_dosname->name_case = 0x0; |
| + if (p_lossy != NULL) *p_lossy = FALSE; |
| + return; |
| + } |
| + |
| + if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_PAR_DIR_NAME)) { |
| + *(dosname) = '.'; |
| + *(dosname+1) = '.'; |
| + p_dosname->name_case = 0x0; |
| + if (p_lossy != NULL) *p_lossy = FALSE; |
| + return; |
| + } |
| + |
| + last_period = NULL; |
| + for (p = uniname; *p; p++) { |
| + if (*p == (UINT16) '.') last_period = p; |
| + } |
| + |
| + i = 0; |
| + while (i < DOS_NAME_LENGTH) { |
| + if (i == 8) { |
| + if (last_period == NULL) break; |
| + |
| + if (uniname <= last_period) { |
| + if (uniname < last_period) lossy = TRUE; |
| + uniname = last_period + 1; |
| + } |
| + } |
| + |
| + if (*uniname == (UINT16) '\0') { |
| + break; |
| + } else if (*uniname == (UINT16) ' ') { |
| + lossy = TRUE; |
| + } else if (*uniname == (UINT16) '.') { |
| + if (uniname < last_period) lossy = TRUE; |
| + else i = 8; |
| + } else if (WSTRCHR(bad_dos_chars, *uniname)) { |
| + lossy = TRUE; |
| + *(dosname+i) = '_'; |
| + i++; |
| + } else { |
| + len = convert_uni_to_ch(nls, buf, *uniname, &lossy); |
| + |
| + if (len > 1) { |
| + if ((i >= 8) && ((i+len) > DOS_NAME_LENGTH)) { |
| + break; |
| + } |
| + if ((i < 8) && ((i+len) > 8)) { |
| + i = 8; |
| + continue; |
| + } |
| + |
| + lower = 0xFF; |
| + |
| + for (j = 0; j < len; j++, i++) { |
| + *(dosname+i) = *(buf+j); |
| + } |
| + } else { |
| + if ((*buf >= 'a') && (*buf <= 'z')) { |
| + *(dosname+i) = *buf - ('a' - 'A'); |
| + |
| + if (i < 8) lower |= 0x08; |
| + else lower |= 0x10; |
| + } else if ((*buf >= 'A') && (*buf <= 'Z')) { |
| + *(dosname+i) = *buf; |
| + |
| + if (i < 8) upper |= 0x08; |
| + else upper |= 0x10; |
| + } else { |
| + *(dosname+i) = *buf; |
| + } |
| + i++; |
| + } |
| + } |
| + |
| + uniname++; |
| + } |
| + |
| + if (*dosname == 0xE5) *dosname = 0x05; |
| + if (*uniname != 0x0) lossy = TRUE; |
| + |
| + if (upper & lower) p_dosname->name_case = 0xFF; |
| + else p_dosname->name_case = lower; |
| + |
| + if (p_lossy != NULL) *p_lossy = lossy; |
| +} |
| + |
| +void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) |
| +{ |
| + INT32 i = 0, j, n = 0; |
| + UINT8 buf[DOS_NAME_LENGTH+2]; |
| + UINT8 *dosname = p_dosname->name; |
| + UINT16 *uniname = p_uniname->name; |
| + struct nls_table *nls = EXFAT_SB(sb)->nls_disk; |
| + |
| + if (*dosname == 0x05) { |
| + *buf = 0xE5; |
| + i++; |
| + n++; |
| + } |
| + |
| + for ( ; i < 8; i++, n++) { |
| + if (*(dosname+i) == ' ') break; |
| + |
| + if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x08)) |
| + *(buf+n) = *(dosname+i) + ('a' - 'A'); |
| + else |
| + *(buf+n) = *(dosname+i); |
| + } |
| + if (*(dosname+8) != ' ') { |
| + *(buf+n) = '.'; |
| + n++; |
| + } |
| + |
| + for (i = 8; i < DOS_NAME_LENGTH; i++, n++) { |
| + if (*(dosname+i) == ' ') break; |
| + |
| + if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x10)) |
| + *(buf+n) = *(dosname+i) + ('a' - 'A'); |
| + else |
| + *(buf+n) = *(dosname+i); |
| + } |
| + *(buf+n) = '\0'; |
| + |
| + i = j = 0; |
| + while (j < (MAX_NAME_LENGTH-1)) { |
| + if (*(buf+i) == '\0') break; |
| + |
| + i += convert_ch_to_uni(nls, uniname, (buf+i), NULL); |
| + |
| + uniname++; |
| + j++; |
| + } |
| + |
| + *uniname = (UINT16) '\0'; |
| +} |
| + |
| +void nls_uniname_to_cstring(struct super_block *sb, UINT8 *p_cstring, UNI_NAME_T *p_uniname) |
| +{ |
| + INT32 i, j, len; |
| + UINT8 buf[MAX_CHARSET_SIZE]; |
| + UINT16 *uniname = p_uniname->name; |
| + struct nls_table *nls = EXFAT_SB(sb)->nls_io; |
| + |
| + i = 0; |
| + while (i < (MAX_NAME_LENGTH-1)) { |
| + if (*uniname == (UINT16) '\0') break; |
| + |
| + len = convert_uni_to_ch(nls, buf, *uniname, NULL); |
| + |
| + if (len > 1) { |
| + for (j = 0; j < len; j++) |
| + *p_cstring++ = (INT8) *(buf+j); |
| + } else { |
| + *p_cstring++ = (INT8) *buf; |
| + } |
| + |
| + uniname++; |
| + i++; |
| + } |
| + |
| + *p_cstring = '\0'; |
| +} |
| + |
| +void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, UINT8 *p_cstring, INT32 *p_lossy) |
| +{ |
| + INT32 i, j, lossy = FALSE; |
| + UINT8 *end_of_name; |
| + UINT16 upname[MAX_NAME_LENGTH]; |
| + UINT16 *uniname = p_uniname->name; |
| + struct nls_table *nls = EXFAT_SB(sb)->nls_io; |
| + |
| + end_of_name = p_cstring + STRLEN((INT8 *) p_cstring); |
| + |
| + while (*(--end_of_name) == ' ') { |
| + if (end_of_name < p_cstring) break; |
| + } |
| + *(++end_of_name) = '\0'; |
| + |
| + if (STRCMP((INT8 *) p_cstring, ".") && STRCMP((INT8 *) p_cstring, "..")) { |
| + while (*(--end_of_name) == '.') { |
| + if (end_of_name < p_cstring) break; |
| + } |
| + *(++end_of_name) = '\0'; |
| + } |
| + |
| + if (*p_cstring == '\0') |
| + lossy = TRUE; |
| + |
| + i = j = 0; |
| + while (j < (MAX_NAME_LENGTH-1)) { |
| + if (*(p_cstring+i) == '\0') break; |
| + |
| + i += convert_ch_to_uni(nls, uniname, (UINT8 *)(p_cstring+i), &lossy); |
| + |
| + if ((*uniname < 0x0020) || WSTRCHR(bad_uni_chars, *uniname)) |
| + lossy = TRUE; |
| + |
| + *(upname+j) = nls_upper(sb, *uniname); |
| + |
| + uniname++; |
| + j++; |
| + } |
| + |
| + if (*(p_cstring+i) != '\0') |
| + lossy = TRUE; |
| + *uniname = (UINT16) '\0'; |
| + |
| + p_uniname->name_len = j; |
| + p_uniname->name_hash = calc_checksum_2byte((void *) upname, j<<1, 0, CS_DEFAULT); |
| + |
| + if (p_lossy != NULL) |
| + *p_lossy = lossy; |
| +} |
| + |
| +static INT32 convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy) |
| +{ |
| + int len; |
| + |
| + *uni = 0x0; |
| + |
| + if (ch[0] < 0x80) { |
| + *uni = (UINT16) ch[0]; |
| + return(1); |
| + } |
| + |
| + if ((len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni)) < 0) { |
| + printk("%s: fail to use nls \n", __func__); |
| + if (lossy != NULL) |
| + *lossy = TRUE; |
| + *uni = (UINT16) '_'; |
| + if (!strcmp(nls->charset, "utf8")) return(1); |
| + else return(2); |
| + } |
| + |
| + return(len); |
| +} |
| + |
| +static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy) |
| +{ |
| + int len; |
| + |
| + ch[0] = 0x0; |
| + |
| + if (uni < 0x0080) { |
| + ch[0] = (UINT8) uni; |
| + return(1); |
| + } |
| + |
| + if ((len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE)) < 0) { |
| + printk("%s: fail to use nls \n", __func__); |
| + if (lossy != NULL) *lossy = TRUE; |
| + ch[0] = '_'; |
| + return(1); |
| + } |
| + |
| + return(len); |
| + |
| +} |
| diff --git a/drivers/staging/exfat/exfat_nls.h b/drivers/staging/exfat/exfat_nls.h |
| new file mode 100644 |
| index 00000000..8a01d269 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_nls.h |
| @@ -0,0 +1,68 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#ifndef _EXFAT_NLS_H |
| +#define _EXFAT_NLS_H |
| + |
| +#include <linux/nls.h> |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#include "exfat_api.h" |
| + |
| +#ifdef __cplusplus |
| +extern "C" { |
| +#endif |
| + |
| +#define NUM_UPCASE 2918 |
| + |
| +#define DOS_CUR_DIR_NAME ". " |
| +#define DOS_PAR_DIR_NAME ".. " |
| + |
| +#if (FFS_CONFIG_LITTLE_ENDIAN == 1) |
| +#define UNI_CUR_DIR_NAME ".\0" |
| +#define UNI_PAR_DIR_NAME ".\0.\0" |
| +#else |
| +#define UNI_CUR_DIR_NAME "\0." |
| +#define UNI_PAR_DIR_NAME "\0.\0." |
| +#endif |
| + |
| + |
| +typedef struct { |
| + UINT8 name[DOS_NAME_LENGTH]; |
| + UINT8 name_case; |
| +} DOS_NAME_T; |
| + |
| +typedef struct { |
| + UINT16 name[MAX_NAME_LENGTH]; |
| + UINT16 name_hash; |
| + UINT8 name_len; |
| +} UNI_NAME_T; |
| + |
| +UINT16 nls_upper(struct super_block *sb, UINT16 a); |
| +INT32 nls_dosname_cmp(struct super_block *sb, UINT8 *a, UINT8 *b); |
| +INT32 nls_uniname_cmp(struct super_block *sb, UINT16 *a, UINT16 *b); |
| +void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, INT32 *p_lossy); |
| +void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); |
| +void nls_uniname_to_cstring(struct super_block *sb, UINT8 *p_cstring, UNI_NAME_T *p_uniname); |
| +void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, UINT8 *p_cstring, INT32 *p_lossy); |
| + |
| +#ifdef __cplusplus |
| +} |
| +#endif |
| +#endif |
| diff --git a/drivers/staging/exfat/exfat_oal.c b/drivers/staging/exfat/exfat_oal.c |
| new file mode 100644 |
| index 00000000..717e4bf8 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_oal.c |
| @@ -0,0 +1,146 @@ |
| +/* Some of the source code in this file came from "linux/fs/fat/misc.c". */ |
| +/* |
| + * linux/fs/fat/misc.c |
| + * |
| + * 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. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#include <linux/semaphore.h> |
| +#include <linux/time.h> |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#include "exfat_api.h" |
| +#include "exfat_oal.h" |
| + |
| +DECLARE_MUTEX(z_sem); |
| + |
| +INT32 sm_init(struct semaphore *sm) |
| +{ |
| + sema_init(sm, 1); |
| + return(0); |
| +} |
| + |
| +INT32 sm_P(struct semaphore *sm) |
| +{ |
| + down(sm); |
| + return 0; |
| +} |
| + |
| +void sm_V(struct semaphore *sm) |
| +{ |
| + up(sm); |
| +} |
| + |
| +extern struct timezone sys_tz; |
| + |
| +#define UNIX_SECS_1980 315532800L |
| + |
| +#if BITS_PER_LONG == 64 |
| +#define UNIX_SECS_2108 4354819200L |
| +#endif |
| + |
| +#define DAYS_DELTA_DECADE (365 * 10 + 2) |
| +#define NO_LEAP_YEAR_2100 (120) |
| +#define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != NO_LEAP_YEAR_2100) |
| + |
| +#define SECS_PER_MIN (60) |
| +#define SECS_PER_HOUR (60 * SECS_PER_MIN) |
| +#define SECS_PER_DAY (24 * SECS_PER_HOUR) |
| + |
| +#define MAKE_LEAP_YEAR(leap_year, year) \ |
| + do { \ |
| + if (unlikely(year > NO_LEAP_YEAR_2100)) \ |
| + leap_year = ((year + 3) / 4) - 1; \ |
| + else \ |
| + leap_year = ((year + 3) / 4); \ |
| + } while(0) |
| + |
| + |
| + |
| +static time_t accum_days_in_year[] = { |
| + 0, 0, 31, 59, 90,120,151,181,212,243,273,304,334, 0, 0, 0, |
| +}; |
| + |
| + |
| +TIMESTAMP_T *tm_current(TIMESTAMP_T *tp) |
| +{ |
| + struct timespec ts = CURRENT_TIME_SEC; |
| + time_t second = ts.tv_sec; |
| + time_t day, leap_day, month, year; |
| + |
| + second -= sys_tz.tz_minuteswest * SECS_PER_MIN; |
| + |
| + if (second < UNIX_SECS_1980) { |
| + tp->sec = 0; |
| + tp->min = 0; |
| + tp->hour = 0; |
| + tp->day = 1; |
| + tp->mon = 1; |
| + tp->year = 0; |
| + return(tp); |
| + } |
| +#if BITS_PER_LONG == 64 |
| + if (second >= UNIX_SECS_2108) { |
| + tp->sec = 59; |
| + tp->min = 59; |
| + tp->hour = 23; |
| + tp->day = 31; |
| + tp->mon = 12; |
| + tp->year = 127; |
| + return(tp); |
| + } |
| +#endif |
| + |
| + day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; |
| + year = day / 365; |
| + |
| + MAKE_LEAP_YEAR(leap_day, year); |
| + if (year * 365 + leap_day > day) |
| + year--; |
| + |
| + MAKE_LEAP_YEAR(leap_day, year); |
| + |
| + day -= year * 365 + leap_day; |
| + |
| + if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { |
| + month = 2; |
| + } else { |
| + if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) |
| + day--; |
| + for (month = 1; month < 12; month++) { |
| + if (accum_days_in_year[month + 1] > day) |
| + break; |
| + } |
| + } |
| + day -= accum_days_in_year[month]; |
| + |
| + tp->sec = second % SECS_PER_MIN; |
| + tp->min = (second / SECS_PER_MIN) % 60; |
| + tp->hour = (second / SECS_PER_HOUR) % 24; |
| + tp->day = day + 1; |
| + tp->mon = month; |
| + tp->year = year; |
| + |
| + return(tp); |
| +} |
| diff --git a/drivers/staging/exfat/exfat_oal.h b/drivers/staging/exfat/exfat_oal.h |
| new file mode 100644 |
| index 00000000..4c7d95b2 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_oal.h |
| @@ -0,0 +1,53 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#ifndef _EXFAT_OAL_H |
| +#define _EXFAT_OAL_H |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#include <linux/version.h> |
| + |
| +#ifdef __cplusplus |
| +extern "C" { |
| +#endif |
| + |
| + typedef struct { |
| + UINT16 sec; |
| + UINT16 min; |
| + UINT16 hour; |
| + UINT16 day; |
| + UINT16 mon; |
| + UINT16 year; |
| + } TIMESTAMP_T; |
| + |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) |
| +#define DECLARE_MUTEX(m) DEFINE_SEMAPHORE(m) |
| +#endif |
| + |
| + INT32 sm_init(struct semaphore *sm); |
| + INT32 sm_P(struct semaphore *sm); |
| + void sm_V(struct semaphore *sm); |
| + |
| + TIMESTAMP_T *tm_current(TIMESTAMP_T *tm); |
| + |
| +#ifdef __cplusplus |
| +} |
| +#endif |
| + |
| +#endif |
| diff --git a/drivers/staging/exfat/exfat_part.h b/drivers/staging/exfat/exfat_part.h |
| new file mode 100644 |
| index 00000000..cc6db65c |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_part.h |
| @@ -0,0 +1,54 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#ifndef _EXFAT_PART_H |
| +#define _EXFAT_PART_H |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#include "exfat_api.h" |
| + |
| +#ifdef __cplusplus |
| +extern "C" { |
| +#endif |
| + |
| +#define MBR_SIGNATURE 0xAA55 |
| + typedef struct { |
| + UINT8 boot_code[446]; |
| + UINT8 partition[64]; |
| + UINT8 signature[2]; |
| + } MBR_SECTOR_T; |
| + |
| + typedef struct { |
| + UINT8 def_boot; |
| + UINT8 bgn_chs[3]; |
| + UINT8 sys_type; |
| + UINT8 end_chs[3]; |
| + UINT8 start_sector[4]; |
| + UINT8 num_sectors[4]; |
| + } PART_ENTRY_T; |
| + |
| + INT32 ffsSetPartition(INT32 dev, INT32 num_vol, PART_INFO_T *vol_spec); |
| + INT32 ffsGetPartition(INT32 dev, INT32 *num_vol, PART_INFO_T *vol_spec); |
| + INT32 ffsGetDevInfo(INT32 dev, DEV_INFO_T *info); |
| + |
| +#ifdef __cplusplus |
| +} |
| +#endif |
| + |
| +#endif |
| diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c |
| new file mode 100644 |
| index 00000000..8c937631 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_super.c |
| @@ -0,0 +1,2154 @@ |
| +/* Some of the source code in this file came from "linux/fs/fat/file.c","linux/fs/fat/inode.c" and "linux/fs/fat/misc.c". */ |
| +/* |
| + * linux/fs/fat/file.c |
| + * |
| + * Written 1992,1993 by Werner Almesberger |
| + * |
| + * regular file handling primitives for fat-based filesystems |
| + */ |
| + |
| +/* |
| + * linux/fs/fat/inode.c |
| + * |
| + * Written 1992,1993 by Werner Almesberger |
| + * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner |
| + * Rewritten for the constant inumbers support by Al Viro |
| + * |
| + * Fixes: |
| + * |
| + * Max Cohan: Fixed invalid FSINFO offset when info_sector is 0 |
| + */ |
| + |
| +/* |
| + * linux/fs/fat/misc.c |
| + * |
| + * 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. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#include <linux/version.h> |
| +#include <linux/module.h> |
| +#include <linux/init.h> |
| +#include <linux/time.h> |
| +#include <linux/slab.h> |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) |
| +#include <linux/smp_lock.h> |
| +#endif |
| +#include <linux/seq_file.h> |
| +#include <linux/pagemap.h> |
| +#include <linux/mpage.h> |
| +#include <linux/buffer_head.h> |
| +#include <linux/exportfs.h> |
| +#include <linux/mount.h> |
| +#include <linux/vfs.h> |
| +#include <linux/parser.h> |
| +#include <linux/uio.h> |
| +#include <linux/writeback.h> |
| +#include <linux/log2.h> |
| +#include <linux/hash.h> |
| +#include <linux/backing-dev.h> |
| +#include <linux/sched.h> |
| +#include <linux/fs_struct.h> |
| +#include <linux/namei.h> |
| +#include <asm/current.h> |
| +#include <asm/unaligned.h> |
| + |
| +#include "exfat_version.h" |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#include "exfat_data.h" |
| +#include "exfat_oal.h" |
| + |
| +#include "exfat_blkdev.h" |
| +#include "exfat_cache.h" |
| +#include "exfat_part.h" |
| +#include "exfat_nls.h" |
| +#include "exfat_api.h" |
| +#include "exfat.h" |
| + |
| +#include "exfat_super.h" |
| + |
| +static struct kmem_cache *exfat_inode_cachep; |
| + |
| +static int exfat_default_codepage = DEFAULT_CODEPAGE; |
| +static char exfat_default_iocharset[] = DEFAULT_IOCHARSET; |
| + |
| +extern struct timezone sys_tz; |
| + |
| +#define CHECK_ERR(x) BUG_ON(x) |
| +#define ELAPSED_TIME 0 |
| + |
| +#if (ELAPSED_TIME == 1) |
| +#include <linux/time.h> |
| + |
| +static UINT32 __t1, __t2; |
| +static UINT32 get_current_msec(void) |
| +{ |
| + struct timeval tm; |
| + do_gettimeofday(&tm); |
| + return((UINT32)(tm.tv_sec*1000000 + tm.tv_usec)); |
| +} |
| +#define TIME_START() do {__t1 = get_current_msec();} while (0) |
| +#define TIME_END() do {__t2 = get_current_msec();} while (0) |
| +#define PRINT_TIME(n) do {printk("[EXFAT] Elapsed time %d = %d (usec)\n", n, (__t2 - __t1));} while (0) |
| +#else |
| +#define TIME_START() |
| +#define TIME_END() |
| +#define PRINT_TIME(n) |
| +#endif |
| + |
| +#define UNIX_SECS_1980 315532800L |
| + |
| +#if BITS_PER_LONG == 64 |
| +#define UNIX_SECS_2108 4354819200L |
| +#endif |
| +#define DAYS_DELTA_DECADE (365 * 10 + 2) |
| +#define NO_LEAP_YEAR_2100 (120) |
| +#define IS_LEAP_YEAR(y) (!((y) & 0x3) && (y) != NO_LEAP_YEAR_2100) |
| + |
| +#define SECS_PER_MIN (60) |
| +#define SECS_PER_HOUR (60 * SECS_PER_MIN) |
| +#define SECS_PER_DAY (24 * SECS_PER_HOUR) |
| + |
| +#define MAKE_LEAP_YEAR(leap_year, year) \ |
| + do { \ |
| + if (unlikely(year > NO_LEAP_YEAR_2100)) \ |
| + leap_year = ((year + 3) / 4) - 1; \ |
| + else \ |
| + leap_year = ((year + 3) / 4); \ |
| + } while(0) |
| + |
| +static time_t accum_days_in_year[] = { |
| + 0, 0, 31, 59, 90,120,151,181,212,243,273,304,334, 0, 0, 0, |
| +}; |
| + |
| +static void _exfat_truncate(struct inode *inode, loff_t old_size); |
| + |
| +void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec *ts, |
| + DATE_TIME_T *tp) |
| +{ |
| + time_t year = tp->Year; |
| + time_t ld; |
| + |
| + MAKE_LEAP_YEAR(ld, year); |
| + |
| + if (IS_LEAP_YEAR(year) && (tp->Month) > 2) |
| + ld++; |
| + |
| + ts->tv_sec = tp->Second + tp->Minute * SECS_PER_MIN |
| + + tp->Hour * SECS_PER_HOUR |
| + + (year * 365 + ld + accum_days_in_year[(tp->Month)] + (tp->Day - 1) + DAYS_DELTA_DECADE) * SECS_PER_DAY |
| + + sys_tz.tz_minuteswest * SECS_PER_MIN; |
| + ts->tv_nsec = 0; |
| +} |
| + |
| +void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec *ts, |
| + DATE_TIME_T *tp) |
| +{ |
| + time_t second = ts->tv_sec; |
| + time_t day, month, year; |
| + time_t ld; |
| + |
| + second -= sys_tz.tz_minuteswest * SECS_PER_MIN; |
| + |
| + if (second < UNIX_SECS_1980) { |
| + tp->Second = 0; |
| + tp->Minute = 0; |
| + tp->Hour = 0; |
| + tp->Day = 1; |
| + tp->Month = 1; |
| + tp->Year = 0; |
| + return; |
| + } |
| +#if (BITS_PER_LONG == 64) |
| + if (second >= UNIX_SECS_2108) { |
| + tp->Second = 59; |
| + tp->Minute = 59; |
| + tp->Hour = 23; |
| + tp->Day = 31; |
| + tp->Month = 12; |
| + tp->Year = 127; |
| + return; |
| + } |
| +#endif |
| + day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; |
| + year = day / 365; |
| + MAKE_LEAP_YEAR(ld, year); |
| + if (year * 365 + ld > day) |
| + year--; |
| + |
| + MAKE_LEAP_YEAR(ld, year); |
| + day -= year * 365 + ld; |
| + |
| + if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { |
| + month = 2; |
| + } else { |
| + if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) |
| + day--; |
| + for (month = 1; month < 12; month++) { |
| + if (accum_days_in_year[month + 1] > day) |
| + break; |
| + } |
| + } |
| + day -= accum_days_in_year[month]; |
| + |
| + tp->Second = second % SECS_PER_MIN; |
| + tp->Minute = (second / SECS_PER_MIN) % 60; |
| + tp->Hour = (second / SECS_PER_HOUR) % 24; |
| + tp->Day = day + 1; |
| + tp->Month = month; |
| + tp->Year = year; |
| +} |
| + |
| +static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos); |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) |
| +static int exfat_generic_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); |
| +#else |
| +static long exfat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); |
| +#endif |
| +static int exfat_sync_inode(struct inode *inode); |
| +static struct inode *exfat_build_inode(struct super_block *sb, FILE_ID_T *fid, loff_t i_pos); |
| +static void exfat_detach(struct inode *inode); |
| +static void exfat_attach(struct inode *inode, loff_t i_pos); |
| +static inline unsigned long exfat_hash(loff_t i_pos); |
| +static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc); |
| +static void exfat_write_super(struct super_block *sb); |
| + |
| +static void __lock_super(struct super_block *sb) |
| +{ |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) |
| + lock_super(sb); |
| +#else |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + mutex_lock(&sbi->s_lock); |
| +#endif |
| +} |
| + |
| +static void __unlock_super(struct super_block *sb) |
| +{ |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) |
| + unlock_super(sb); |
| +#else |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + mutex_unlock(&sbi->s_lock); |
| +#endif |
| +} |
| + |
| +static int __is_sb_dirty(struct super_block *sb) |
| +{ |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) |
| + return sb->s_dirt; |
| +#else |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + return sbi->s_dirt; |
| +#endif |
| +} |
| + |
| +static void __set_sb_clean(struct super_block *sb) |
| +{ |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) |
| + sb->s_dirt = 0; |
| +#else |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + sbi->s_dirt = 0; |
| +#endif |
| +} |
| + |
| +static int exfat_readdir(struct file *filp, void *dirent, filldir_t filldir) |
| +{ |
| + struct inode *inode = filp->f_path.dentry->d_inode; |
| + struct super_block *sb = inode->i_sb; |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + FS_INFO_T *p_fs = &(sbi->fs_info); |
| + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); |
| + DIR_ENTRY_T de; |
| + unsigned long inum; |
| + loff_t cpos; |
| + int err = 0; |
| + |
| + __lock_super(sb); |
| + |
| + cpos = filp->f_pos; |
| + if ((p_fs->vol_type == EXFAT) || (inode->i_ino == EXFAT_ROOT_INO)) { |
| + while (cpos < 2) { |
| + if (inode->i_ino == EXFAT_ROOT_INO) |
| + inum = EXFAT_ROOT_INO; |
| + else if (cpos == 0) |
| + inum = inode->i_ino; |
| + else |
| + inum = parent_ino(filp->f_path.dentry); |
| + |
| + if (filldir(dirent, "..", cpos+1, cpos, inum, DT_DIR) < 0) |
| + goto out; |
| + cpos++; |
| + filp->f_pos++; |
| + } |
| + if (cpos == 2) { |
| + cpos = 0; |
| + } |
| + } |
| + if (cpos & (DENTRY_SIZE - 1)) { |
| + err = -ENOENT; |
| + goto out; |
| + } |
| + |
| +get_new: |
| + EXFAT_I(inode)->fid.size = i_size_read(inode); |
| + EXFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS; |
| + |
| + err = FsReadDir(inode, &de); |
| + if (err) { |
| + if (err == FFS_MEDIAERR) { |
| + cpos += 1 << p_bd->sector_size_bits; |
| + cpos &= ~((1 << p_bd->sector_size_bits)-1); |
| + } |
| + |
| + err = -EIO; |
| + goto end_of_dir; |
| + } |
| + |
| + cpos = EXFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS; |
| + |
| + if (!de.Name[0]) |
| + goto end_of_dir; |
| + |
| + if (!memcmp(de.ShortName, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { |
| + inum = inode->i_ino; |
| + } else if (!memcmp(de.ShortName, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { |
| + inum = parent_ino(filp->f_path.dentry); |
| + } else { |
| + loff_t i_pos = ((loff_t) EXFAT_I(inode)->fid.start_clu << 32) | |
| + ((EXFAT_I(inode)->fid.rwoffset-1) & 0xffffffff); |
| + |
| + struct inode *tmp = exfat_iget(sb, i_pos); |
| + if (tmp) { |
| + inum = tmp->i_ino; |
| + iput(tmp); |
| + } else { |
| + inum = iunique(sb, EXFAT_ROOT_INO); |
| + } |
| + } |
| + |
| + if (filldir(dirent, de.Name, strlen(de.Name), cpos-1, inum, |
| + (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG) < 0) |
| + goto out; |
| + |
| + filp->f_pos = cpos; |
| + goto get_new; |
| + |
| +end_of_dir: |
| + filp->f_pos = cpos; |
| +out: |
| + __unlock_super(sb); |
| + return err; |
| +} |
| + |
| +static int exfat_ioctl_volume_id(struct inode *dir) |
| +{ |
| + struct super_block *sb = dir->i_sb; |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + FS_INFO_T *p_fs = &(sbi->fs_info); |
| + |
| + return p_fs->vol_id; |
| +} |
| + |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) |
| +static int exfat_generic_ioctl(struct inode *inode, struct file *filp, |
| + unsigned int cmd, unsigned long arg) |
| +#else |
| +static long exfat_generic_ioctl(struct file *filp, |
| + unsigned int cmd, unsigned long arg) |
| +#endif |
| +{ |
| +#if EXFAT_CONFIG_KERNEL_DEBUG |
| +#if !(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) |
| + struct inode *inode = filp->f_dentry->d_inode; |
| +#endif |
| + unsigned int flags; |
| +#endif |
| + |
| + switch (cmd) { |
| + case EXFAT_IOCTL_GET_VOLUME_ID: |
| + return exfat_ioctl_volume_id(inode); |
| +#if EXFAT_CONFIG_KERNEL_DEBUG |
| + case EXFAT_IOC_GET_DEBUGFLAGS: { |
| + struct super_block *sb = inode->i_sb; |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + |
| + flags = sbi->debug_flags; |
| + return put_user(flags, (int __user *)arg); |
| + } |
| + case EXFAT_IOC_SET_DEBUGFLAGS: { |
| + struct super_block *sb = inode->i_sb; |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + |
| + if (!capable(CAP_SYS_ADMIN)) |
| + return -EPERM; |
| + |
| + if (get_user(flags, (int __user *) arg)) |
| + return -EFAULT; |
| + |
| + __lock_super(sb); |
| + sbi->debug_flags = flags; |
| + __unlock_super(sb); |
| + |
| + return 0; |
| + } |
| +#endif |
| + default: |
| + return -ENOTTY; |
| + } |
| +} |
| + |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) |
| +static int exfat_file_fsync(struct file *filp, int datasync) |
| +{ |
| + struct inode *inode = filp->f_mapping->host; |
| + struct super_block *sb = inode->i_sb; |
| + int res, err; |
| + |
| + res = generic_file_fsync(filp, datasync); |
| + err = FsSyncVol(sb, 1); |
| + |
| + return res ? res : err; |
| +} |
| +#endif |
| + |
| +const struct file_operations exfat_dir_operations = { |
| + .llseek = generic_file_llseek, |
| + .read = generic_read_dir, |
| + .readdir = exfat_readdir, |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) |
| + .ioctl = exfat_generic_ioctl, |
| + .fsync = exfat_file_fsync, |
| +#else |
| + .unlocked_ioctl = exfat_generic_ioctl, |
| + .fsync = generic_file_fsync, |
| +#endif |
| +}; |
| + |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) |
| +static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, |
| + bool excl) |
| +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) |
| +static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, |
| + struct nameidata *nd) |
| +#else |
| +static int exfat_create(struct inode *dir, struct dentry *dentry, int mode, |
| + struct nameidata *nd) |
| +#endif |
| +{ |
| + struct super_block *sb = dir->i_sb; |
| + struct inode *inode; |
| + struct timespec ts; |
| + FILE_ID_T fid; |
| + loff_t i_pos; |
| + int err; |
| + |
| + __lock_super(sb); |
| + |
| + PRINTK("exfat_create entered\n"); |
| + |
| + ts = CURRENT_TIME_SEC; |
| + |
| + err = FsCreateFile(dir, (UINT8 *) dentry->d_name.name, FM_REGULAR, &fid); |
| + if (err) { |
| + if (err == FFS_INVALIDPATH) |
| + err = -EINVAL; |
| + else if (err == FFS_FILEEXIST) |
| + err = -EEXIST; |
| + else if (err == FFS_FULL) |
| + err = -ENOSPC; |
| + else if (err == FFS_NAMETOOLONG) |
| + err = -ENAMETOOLONG; |
| + else |
| + err = -EIO; |
| + goto out; |
| + } |
| + dir->i_version++; |
| + dir->i_ctime = dir->i_mtime = dir->i_atime = ts; |
| + if (IS_DIRSYNC(dir)) |
| + (void) exfat_sync_inode(dir); |
| + else |
| + mark_inode_dirty(dir); |
| + |
| + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); |
| + |
| + inode = exfat_build_inode(sb, &fid, i_pos); |
| + if (IS_ERR(inode)) { |
| + err = PTR_ERR(inode); |
| + goto out; |
| + } |
| + inode->i_version++; |
| + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; |
| + |
| + dentry->d_time = dentry->d_parent->d_inode->i_version; |
| + d_instantiate(dentry, inode); |
| + |
| +out: |
| + __unlock_super(sb); |
| + PRINTK("exfat_create exited\n"); |
| + return err; |
| +} |
| + |
| +static int exfat_find(struct inode *dir, struct qstr *qname, |
| + FILE_ID_T *fid) |
| +{ |
| + int err; |
| + |
| + if (qname->len == 0) |
| + return -ENOENT; |
| + |
| + err = FsLookupFile(dir, (UINT8 *) qname->name, fid); |
| + if (err) |
| + return -ENOENT; |
| + |
| + return 0; |
| +} |
| + |
| +static int exfat_d_anon_disconn(struct dentry *dentry) |
| +{ |
| + return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED); |
| +} |
| + |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) |
| +static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, |
| + unsigned int flags) |
| +#else |
| +static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, |
| + struct nameidata *nd) |
| +#endif |
| +{ |
| + struct super_block *sb = dir->i_sb; |
| + struct inode *inode; |
| + struct dentry *alias; |
| + int err; |
| + FILE_ID_T fid; |
| + loff_t i_pos; |
| + UINT64 ret; |
| + mode_t i_mode; |
| + |
| + __lock_super(sb); |
| + PRINTK("exfat_lookup entered\n"); |
| + err = exfat_find(dir, &dentry->d_name, &fid); |
| + if (err) { |
| + if (err == -ENOENT) { |
| + inode = NULL; |
| + goto out; |
| + } |
| + goto error; |
| + } |
| + |
| + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); |
| + inode = exfat_build_inode(sb, &fid, i_pos); |
| + if (IS_ERR(inode)) { |
| + err = PTR_ERR(inode); |
| + goto error; |
| + } |
| + |
| + i_mode = inode->i_mode; |
| + if (S_ISLNK(i_mode)) { |
| + EXFAT_I(inode)->target = MALLOC(i_size_read(inode)+1); |
| + if (!EXFAT_I(inode)->target) { |
| + err = -ENOMEM; |
| + goto error; |
| + } |
| + FsReadFile(dir, &fid, EXFAT_I(inode)->target, i_size_read(inode), &ret); |
| + *(EXFAT_I(inode)->target + i_size_read(inode)) = '\0'; |
| + } |
| + |
| + alias = d_find_alias(inode); |
| + if (alias && !exfat_d_anon_disconn(alias)) { |
| + CHECK_ERR(d_unhashed(alias)); |
| + if (!S_ISDIR(i_mode)) |
| + d_move(alias, dentry); |
| + iput(inode); |
| + __unlock_super(sb); |
| + PRINTK("exfat_lookup exited 1\n"); |
| + return alias; |
| + } else { |
| + dput(alias); |
| + } |
| +out: |
| + __unlock_super(sb); |
| + dentry->d_time = dentry->d_parent->d_inode->i_version; |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) |
| + dentry->d_op = sb->s_root->d_op; |
| + dentry = d_splice_alias(inode, dentry); |
| + if (dentry) { |
| + dentry->d_op = sb->s_root->d_op; |
| + dentry->d_time = dentry->d_parent->d_inode->i_version; |
| + } |
| +#else |
| + dentry = d_splice_alias(inode, dentry); |
| + if (dentry) |
| + dentry->d_time = dentry->d_parent->d_inode->i_version; |
| +#endif |
| + PRINTK("exfat_lookup exited 2\n"); |
| + return dentry; |
| + |
| +error: |
| + __unlock_super(sb); |
| + PRINTK("exfat_lookup exited 3\n"); |
| + return ERR_PTR(err); |
| +} |
| + |
| +static int exfat_unlink(struct inode *dir, struct dentry *dentry) |
| +{ |
| + struct inode *inode = dentry->d_inode; |
| + struct super_block *sb = dir->i_sb; |
| + struct timespec ts; |
| + int err; |
| + |
| + __lock_super(sb); |
| + |
| + PRINTK("exfat_unlink entered\n"); |
| + |
| + ts = CURRENT_TIME_SEC; |
| + |
| + EXFAT_I(inode)->fid.size = i_size_read(inode); |
| + |
| + err = FsRemoveFile(dir, &(EXFAT_I(inode)->fid)); |
| + if (err) { |
| + if (err == FFS_PERMISSIONERR) |
| + err = -EPERM; |
| + else |
| + err = -EIO; |
| + goto out; |
| + } |
| + dir->i_version++; |
| + dir->i_mtime = dir->i_atime = ts; |
| + if (IS_DIRSYNC(dir)) |
| + (void) exfat_sync_inode(dir); |
| + else |
| + mark_inode_dirty(dir); |
| + |
| + clear_nlink(inode); |
| + inode->i_mtime = inode->i_atime = ts; |
| + exfat_detach(inode); |
| + remove_inode_hash(inode); |
| + |
| +out: |
| + __unlock_super(sb); |
| + PRINTK("exfat_unlink exited\n"); |
| + return err; |
| +} |
| + |
| +static int exfat_symlink(struct inode *dir, struct dentry *dentry, const char *target) |
| +{ |
| + struct super_block *sb = dir->i_sb; |
| + struct inode *inode; |
| + struct timespec ts; |
| + FILE_ID_T fid; |
| + loff_t i_pos; |
| + int err; |
| + UINT64 len = (UINT64) strlen(target); |
| + UINT64 ret; |
| + |
| + __lock_super(sb); |
| + |
| + PRINTK("exfat_symlink entered\n"); |
| + |
| + ts = CURRENT_TIME_SEC; |
| + |
| + err = FsCreateFile(dir, (UINT8 *) dentry->d_name.name, FM_SYMLINK, &fid); |
| + if (err) { |
| + if (err == FFS_INVALIDPATH) |
| + err = -EINVAL; |
| + else if (err == FFS_FILEEXIST) |
| + err = -EEXIST; |
| + else if (err == FFS_FULL) |
| + err = -ENOSPC; |
| + else |
| + err = -EIO; |
| + goto out; |
| + } |
| + |
| + err = FsWriteFile(dir, &fid, (char *) target, len, &ret); |
| + |
| + if (err) { |
| + FsRemoveFile(dir, &fid); |
| + |
| + if (err == FFS_FULL) |
| + err = -ENOSPC; |
| + else |
| + err = -EIO; |
| + goto out; |
| + } |
| + |
| + dir->i_version++; |
| + dir->i_ctime = dir->i_mtime = dir->i_atime = ts; |
| + if (IS_DIRSYNC(dir)) |
| + (void) exfat_sync_inode(dir); |
| + else |
| + mark_inode_dirty(dir); |
| + |
| + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); |
| + |
| + inode = exfat_build_inode(sb, &fid, i_pos); |
| + if (IS_ERR(inode)) { |
| + err = PTR_ERR(inode); |
| + goto out; |
| + } |
| + inode->i_version++; |
| + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; |
| + |
| + EXFAT_I(inode)->target = MALLOC(len+1); |
| + if (!EXFAT_I(inode)->target) { |
| + err = -ENOMEM; |
| + goto out; |
| + } |
| + MEMCPY(EXFAT_I(inode)->target, target, len+1); |
| + |
| + dentry->d_time = dentry->d_parent->d_inode->i_version; |
| + d_instantiate(dentry, inode); |
| + |
| +out: |
| + __unlock_super(sb); |
| + PRINTK("exfat_symlink exited\n"); |
| + return err; |
| +} |
| + |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) |
| +static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) |
| +#else |
| +static int exfat_mkdir(struct inode *dir, struct dentry *dentry, int mode) |
| +#endif |
| +{ |
| + struct super_block *sb = dir->i_sb; |
| + struct inode *inode; |
| + struct timespec ts; |
| + FILE_ID_T fid; |
| + loff_t i_pos; |
| + int err; |
| + |
| + __lock_super(sb); |
| + |
| + PRINTK("exfat_mkdir entered\n"); |
| + |
| + ts = CURRENT_TIME_SEC; |
| + |
| + err = FsCreateDir(dir, (UINT8 *) dentry->d_name.name, &fid); |
| + if (err) { |
| + if (err == FFS_INVALIDPATH) |
| + err = -EINVAL; |
| + else if (err == FFS_FILEEXIST) |
| + err = -EEXIST; |
| + else if (err == FFS_FULL) |
| + err = -ENOSPC; |
| + else if (err == FFS_NAMETOOLONG) |
| + err = -ENAMETOOLONG; |
| + else |
| + err = -EIO; |
| + goto out; |
| + } |
| + dir->i_version++; |
| + dir->i_ctime = dir->i_mtime = dir->i_atime = ts; |
| + if (IS_DIRSYNC(dir)) |
| + (void) exfat_sync_inode(dir); |
| + else |
| + mark_inode_dirty(dir); |
| + inc_nlink(dir); |
| + |
| + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); |
| + |
| + inode = exfat_build_inode(sb, &fid, i_pos); |
| + if (IS_ERR(inode)) { |
| + err = PTR_ERR(inode); |
| + goto out; |
| + } |
| + inode->i_version++; |
| + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; |
| + |
| + dentry->d_time = dentry->d_parent->d_inode->i_version; |
| + d_instantiate(dentry, inode); |
| + |
| +out: |
| + __unlock_super(sb); |
| + PRINTK("exfat_mkdir exited\n"); |
| + return err; |
| +} |
| + |
| +static int exfat_rmdir(struct inode *dir, struct dentry *dentry) |
| +{ |
| + struct inode *inode = dentry->d_inode; |
| + struct super_block *sb = dir->i_sb; |
| + struct timespec ts; |
| + int err; |
| + |
| + __lock_super(sb); |
| + |
| + PRINTK("exfat_rmdir entered\n"); |
| + |
| + ts = CURRENT_TIME_SEC; |
| + |
| + EXFAT_I(inode)->fid.size = i_size_read(inode); |
| + |
| + err = FsRemoveDir(dir, &(EXFAT_I(inode)->fid)); |
| + if (err) { |
| + if (err == FFS_INVALIDPATH) |
| + err = -EINVAL; |
| + else if (err == FFS_FILEEXIST) |
| + err = -ENOTEMPTY; |
| + else if (err == FFS_NOTFOUND) |
| + err = -ENOENT; |
| + else if (err == FFS_DIRBUSY) |
| + err = -EBUSY; |
| + else |
| + err = -EIO; |
| + goto out; |
| + } |
| + dir->i_version++; |
| + dir->i_mtime = dir->i_atime = ts; |
| + if (IS_DIRSYNC(dir)) |
| + (void) exfat_sync_inode(dir); |
| + else |
| + mark_inode_dirty(dir); |
| + drop_nlink(dir); |
| + |
| + clear_nlink(inode); |
| + inode->i_mtime = inode->i_atime = ts; |
| + exfat_detach(inode); |
| + remove_inode_hash(inode); |
| + |
| +out: |
| + __unlock_super(sb); |
| + PRINTK("exfat_rmdir exited\n"); |
| + return err; |
| +} |
| + |
| +static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, |
| + struct inode *new_dir, struct dentry *new_dentry) |
| +{ |
| + struct inode *old_inode, *new_inode; |
| + struct super_block *sb = old_dir->i_sb; |
| + struct timespec ts; |
| + loff_t i_pos; |
| + int err; |
| + |
| + __lock_super(sb); |
| + |
| + PRINTK("exfat_rename entered\n"); |
| + |
| + old_inode = old_dentry->d_inode; |
| + new_inode = new_dentry->d_inode; |
| + |
| + ts = CURRENT_TIME_SEC; |
| + |
| + EXFAT_I(old_inode)->fid.size = i_size_read(old_inode); |
| + |
| + err = FsMoveFile(old_dir, &(EXFAT_I(old_inode)->fid), new_dir, new_dentry); |
| + if (err) { |
| + if (err == FFS_PERMISSIONERR) |
| + err = -EPERM; |
| + else if (err == FFS_INVALIDPATH) |
| + err = -EINVAL; |
| + else if (err == FFS_FILEEXIST) |
| + err = -EEXIST; |
| + else if (err == FFS_NOTFOUND) |
| + err = -ENOENT; |
| + else if (err == FFS_FULL) |
| + err = -ENOSPC; |
| + else |
| + err = -EIO; |
| + goto out; |
| + } |
| + new_dir->i_version++; |
| + new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime = ts; |
| + if (IS_DIRSYNC(new_dir)) |
| + (void) exfat_sync_inode(new_dir); |
| + else |
| + mark_inode_dirty(new_dir); |
| + |
| + i_pos = ((loff_t) EXFAT_I(old_inode)->fid.dir.dir << 32) | |
| + (EXFAT_I(old_inode)->fid.entry & 0xffffffff); |
| + |
| + exfat_detach(old_inode); |
| + exfat_attach(old_inode, i_pos); |
| + if (IS_DIRSYNC(new_dir)) |
| + (void) 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); |
| + } |
| + |
| + old_dir->i_version++; |
| + old_dir->i_ctime = old_dir->i_mtime = ts; |
| + if (IS_DIRSYNC(old_dir)) |
| + (void) exfat_sync_inode(old_dir); |
| + else |
| + mark_inode_dirty(old_dir); |
| + |
| + if (new_inode) { |
| + exfat_detach(new_inode); |
| + drop_nlink(new_inode); |
| + if (S_ISDIR(new_inode->i_mode)) |
| + drop_nlink(new_inode); |
| + new_inode->i_ctime = ts; |
| + } |
| + |
| +out: |
| + __unlock_super(sb); |
| + PRINTK("exfat_rename exited\n"); |
| + return err; |
| +} |
| + |
| +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; |
| + |
| + if ((err = generic_cont_expand_simple(inode, size)) != 0) |
| + return err; |
| + |
| + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; |
| + mark_inode_dirty(inode); |
| + |
| + if (IS_SYNC(inode)) { |
| + err = filemap_fdatawrite_range(mapping, start, start + count - 1); |
| + err2 = sync_mapping_buffers(mapping); |
| + err = (err)?(err):(err2); |
| + err2 = write_inode_now(inode, 1); |
| + err = (err)?(err):(err2); |
| + if (!err) { |
| + err = filemap_fdatawait_range(mapping, start, start + count - 1); |
| + } |
| + } |
| + return err; |
| +} |
| + |
| +static int exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode) |
| +{ |
| + mode_t allow_utime = sbi->options.allow_utime; |
| + |
| + if (current_fsuid() != inode->i_uid) { |
| + if (in_group_p(inode->i_gid)) |
| + allow_utime >>= 3; |
| + if (allow_utime & MAY_WRITE) |
| + return 1; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +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; |
| + |
| + if (S_ISREG(i_mode) || S_ISLNK(i_mode)) |
| + mask = sbi->options.fs_fmask; |
| + else |
| + mask = sbi->options.fs_dmask; |
| + |
| + perm = *mode_ptr & ~(S_IFMT | mask); |
| + |
| + if ((perm & (S_IRUGO | S_IXUGO)) != (i_mode & (S_IRUGO|S_IXUGO))) |
| + return -EPERM; |
| + |
| + if (exfat_mode_can_hold_ro(inode)) { |
| + if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask))) |
| + return -EPERM; |
| + } else { |
| + if ((perm & S_IWUGO) != (S_IWUGO & ~mask)) |
| + return -EPERM; |
| + } |
| + |
| + *mode_ptr &= S_IFMT | perm; |
| + |
| + return 0; |
| +} |
| + |
| +static int exfat_setattr(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; |
| + loff_t old_size; |
| + |
| + PRINTK("exfat_setattr entered\n"); |
| + |
| + 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; |
| + } |
| + |
| + 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 = inode_change_ok(inode, attr); |
| + attr->ia_valid = ia_valid; |
| + if (error) { |
| + return error; |
| + } |
| + |
| + if (((attr->ia_valid & ATTR_UID) && |
| + (attr->ia_uid != sbi->options.fs_uid)) || |
| + ((attr->ia_valid & ATTR_GID) && |
| + (attr->ia_gid != sbi->options.fs_gid)) || |
| + ((attr->ia_valid & ATTR_MODE) && |
| + (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | S_IRWXUGO)))) { |
| + return -EPERM; |
| + } |
| + |
| + if (attr->ia_valid & ATTR_MODE) { |
| + if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0) |
| + attr->ia_valid &= ~ATTR_MODE; |
| + } |
| + |
| + EXFAT_I(inode)->fid.size = i_size_read(inode); |
| + |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) |
| + if (attr->ia_valid) |
| + error = inode_setattr(inode, attr); |
| +#else |
| + if (attr->ia_valid & ATTR_SIZE) { |
| + old_size = i_size_read(inode); |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) |
| + down_write(&EXFAT_I(inode)->truncate_lock); |
| + truncate_setsize(inode, attr->ia_size); |
| + _exfat_truncate(inode, old_size); |
| + up_write(&EXFAT_I(inode)->truncate_lock); |
| +#else |
| + truncate_setsize(inode, attr->ia_size); |
| + _exfat_truncate(inode, old_size); |
| +#endif |
| + } |
| + setattr_copy(inode, attr); |
| + mark_inode_dirty(inode); |
| +#endif |
| + |
| + |
| + PRINTK("exfat_setattr exited\n"); |
| + return error; |
| +} |
| + |
| +static int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) |
| +{ |
| + struct inode *inode = dentry->d_inode; |
| + |
| + PRINTK("exfat_getattr entered\n"); |
| + |
| + generic_fillattr(inode, stat); |
| + stat->blksize = EXFAT_SB(inode->i_sb)->fs_info.cluster_size; |
| + |
| + PRINTK("exfat_getattr exited\n"); |
| + return 0; |
| +} |
| + |
| +const struct inode_operations exfat_dir_inode_operations = { |
| + .create = exfat_create, |
| + .lookup = exfat_lookup, |
| + .unlink = exfat_unlink, |
| + .symlink = exfat_symlink, |
| + .mkdir = exfat_mkdir, |
| + .rmdir = exfat_rmdir, |
| + .rename = exfat_rename, |
| + .setattr = exfat_setattr, |
| + .getattr = exfat_getattr, |
| +}; |
| + |
| +static void *exfat_follow_link(struct dentry *dentry, struct nameidata *nd) |
| +{ |
| + struct exfat_inode_info *ei = EXFAT_I(dentry->d_inode); |
| + nd_set_link(nd, (char *)(ei->target)); |
| + return NULL; |
| +} |
| + |
| +const struct inode_operations exfat_symlink_inode_operations = { |
| + .readlink = generic_readlink, |
| + .follow_link = exfat_follow_link, |
| +}; |
| + |
| +static int exfat_file_release(struct inode *inode, struct file *filp) |
| +{ |
| + struct super_block *sb = inode->i_sb; |
| + |
| + EXFAT_I(inode)->fid.size = i_size_read(inode); |
| + FsSyncVol(sb, 0); |
| + return 0; |
| +} |
| + |
| +const struct file_operations exfat_file_operations = { |
| + .llseek = generic_file_llseek, |
| + .read = do_sync_read, |
| + .write = do_sync_write, |
| + .aio_read = generic_file_aio_read, |
| + .aio_write = generic_file_aio_write, |
| + .mmap = generic_file_mmap, |
| + .release = exfat_file_release, |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) |
| + .ioctl = exfat_generic_ioctl, |
| + .fsync = exfat_file_fsync, |
| +#else |
| + .unlocked_ioctl = exfat_generic_ioctl, |
| + .fsync = generic_file_fsync, |
| +#endif |
| + .splice_read = generic_file_splice_read, |
| +}; |
| + |
| +static void _exfat_truncate(struct inode *inode, loff_t old_size) |
| +{ |
| + struct super_block *sb = inode->i_sb; |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + FS_INFO_T *p_fs = &(sbi->fs_info); |
| + int err; |
| + |
| + __lock_super(sb); |
| + |
| + if (EXFAT_I(inode)->mmu_private > i_size_read(inode)) |
| + EXFAT_I(inode)->mmu_private = i_size_read(inode); |
| + |
| + if (EXFAT_I(inode)->fid.start_clu == 0) goto out; |
| + |
| + err = FsTruncateFile(inode, old_size, i_size_read(inode)); |
| + if (err) goto out; |
| + |
| + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; |
| + if (IS_DIRSYNC(inode)) |
| + (void) exfat_sync_inode(inode); |
| + else |
| + mark_inode_dirty(inode); |
| + |
| + inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) |
| + & ~((loff_t)p_fs->cluster_size - 1)) >> 9; |
| +out: |
| + __unlock_super(sb); |
| +} |
| + |
| +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36) |
| +static void exfat_truncate(struct inode *inode) |
| +{ |
| + _exfat_truncate(inode, i_size_read(inode)); |
| +} |
| +#endif |
| + |
| +const struct inode_operations exfat_file_inode_operations = { |
| +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36) |
| + .truncate = exfat_truncate, |
| +#endif |
| + .setattr = exfat_setattr, |
| + .getattr = exfat_getattr, |
| +}; |
| + |
| +static int exfat_bmap(struct inode *inode, sector_t sector, sector_t *phys, |
| + unsigned long *mapped_blocks, int *create) |
| +{ |
| + struct super_block *sb = inode->i_sb; |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + FS_INFO_T *p_fs = &(sbi->fs_info); |
| + BD_INFO_T *p_bd = &(sbi->bd_info); |
| + const unsigned long blocksize = sb->s_blocksize; |
| + const unsigned char blocksize_bits = sb->s_blocksize_bits; |
| + sector_t last_block; |
| + int err, clu_offset, sec_offset; |
| + unsigned int cluster; |
| + |
| + *phys = 0; |
| + *mapped_blocks = 0; |
| + |
| + if ((p_fs->vol_type == FAT12) || (p_fs->vol_type == FAT16)) { |
| + if (inode->i_ino == EXFAT_ROOT_INO) { |
| + if (sector < (p_fs->dentries_in_root >> (p_bd->sector_size_bits-DENTRY_SIZE_BITS))) { |
| + *phys = sector + p_fs->root_start_sector; |
| + *mapped_blocks = 1; |
| + } |
| + return 0; |
| + } |
| + } |
| + |
| + last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits; |
| + if (sector >= last_block) { |
| + if (*create == 0) return 0; |
| + } else { |
| + *create = 0; |
| + } |
| + |
| + clu_offset = sector >> p_fs->sectors_per_clu_bits; |
| + sec_offset = sector & (p_fs->sectors_per_clu - 1); |
| + |
| + EXFAT_I(inode)->fid.size = i_size_read(inode); |
| + |
| + err = FsMapCluster(inode, clu_offset, &cluster); |
| + |
| + if (err) { |
| + if (err == FFS_FULL) |
| + return -ENOSPC; |
| + else |
| + return -EIO; |
| + } else if (cluster != CLUSTER_32(~0)) { |
| + *phys = START_SECTOR(cluster) + sec_offset; |
| + *mapped_blocks = p_fs->sectors_per_clu - sec_offset; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static int exfat_get_block(struct inode *inode, sector_t iblock, |
| + struct buffer_head *bh_result, int create) |
| +{ |
| + struct super_block *sb = inode->i_sb; |
| + unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; |
| + int err; |
| + unsigned long mapped_blocks; |
| + sector_t phys; |
| + |
| + __lock_super(sb); |
| + |
| + err = exfat_bmap(inode, iblock, &phys, &mapped_blocks, &create); |
| + if (err) { |
| + __unlock_super(sb); |
| + return err; |
| + } |
| + |
| + if (phys) { |
| + max_blocks = min(mapped_blocks, max_blocks); |
| + if (create) { |
| + EXFAT_I(inode)->mmu_private += max_blocks << sb->s_blocksize_bits; |
| + set_buffer_new(bh_result); |
| + } |
| + map_bh(bh_result, sb, phys); |
| + } |
| + |
| + bh_result->b_size = max_blocks << sb->s_blocksize_bits; |
| + __unlock_super(sb); |
| + |
| + return 0; |
| +} |
| + |
| +static int exfat_readpage(struct file *file, struct page *page) |
| +{ |
| + int ret; |
| + ret = mpage_readpage(page, exfat_get_block); |
| + return ret; |
| +} |
| + |
| +static int exfat_readpages(struct file *file, struct address_space *mapping, |
| + struct list_head *pages, unsigned nr_pages) |
| +{ |
| + int ret; |
| + ret = mpage_readpages(mapping, pages, nr_pages, exfat_get_block); |
| + return ret; |
| +} |
| + |
| +static int exfat_writepage(struct page *page, struct writeback_control *wbc) |
| +{ |
| + int ret; |
| + ret = block_write_full_page(page, exfat_get_block, wbc); |
| + return ret; |
| +} |
| + |
| +static int exfat_writepages(struct address_space *mapping, |
| + struct writeback_control *wbc) |
| +{ |
| + int ret; |
| + ret = mpage_writepages(mapping, wbc, exfat_get_block); |
| + return ret; |
| +} |
| + |
| +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) |
| +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, to, i_size_read(inode)); |
| + EXFAT_I(inode)->fid.size = i_size_read(inode); |
| + _exfat_truncate(inode, i_size_read(inode)); |
| + } |
| +} |
| +#endif |
| + |
| + |
| +static int exfat_write_begin(struct file *file, struct address_space *mapping, |
| + loff_t pos, unsigned len, unsigned 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)->mmu_private); |
| + |
| +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) |
| + if (ret < 0) |
| + exfat_write_failed(mapping, pos+len); |
| +#endif |
| + return ret; |
| +} |
| + |
| +static int exfat_write_end(struct file *file, struct address_space *mapping, |
| + loff_t pos, unsigned len, unsigned copied, |
| + struct page *pagep, void *fsdata) |
| +{ |
| + struct inode *inode = mapping->host; |
| + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); |
| + int err; |
| + |
| + err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata); |
| + |
| +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) |
| + if (err < len) |
| + exfat_write_failed(mapping, pos+len); |
| +#endif |
| + |
| + if (!(err < 0) && !(fid->attr & ATTR_ARCHIVE)) { |
| + inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; |
| + fid->attr |= ATTR_ARCHIVE; |
| + mark_inode_dirty(inode); |
| + } |
| + return err; |
| +} |
| + |
| +static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, |
| + const struct iovec *iov, |
| + loff_t offset, unsigned long nr_segs) |
| +{ |
| + struct inode *inode = iocb->ki_filp->f_mapping->host; |
| +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) |
| + struct address_space *mapping = iocb->ki_filp->f_mapping; |
| +#endif |
| + ssize_t ret; |
| + |
| + if (rw == WRITE) { |
| + if (EXFAT_I(inode)->mmu_private < (offset + iov_length(iov, nr_segs))) |
| + return 0; |
| + } |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) |
| + ret = blockdev_direct_IO(rw, iocb, inode, iov, |
| + offset, nr_segs, exfat_get_block); |
| +#else |
| + ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, |
| + offset, nr_segs, exfat_get_block, NULL); |
| +#endif |
| + |
| +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) |
| + if ((ret < 0) && (rw & WRITE)) |
| + exfat_write_failed(mapping, offset+iov_length(iov, nr_segs)); |
| +#endif |
| + return ret; |
| + |
| +} |
| + |
| +static sector_t _exfat_bmap(struct address_space *mapping, sector_t block) |
| +{ |
| + sector_t blocknr; |
| + |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) |
| + 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); |
| +#else |
| + down_read(&mapping->host->i_alloc_sem); |
| + blocknr = generic_block_bmap(mapping, block, exfat_get_block); |
| + up_read(&mapping->host->i_alloc_sem); |
| +#endif |
| + |
| + return blocknr; |
| +} |
| + |
| +const struct address_space_operations exfat_aops = { |
| + .readpage = exfat_readpage, |
| + .readpages = exfat_readpages, |
| + .writepage = exfat_writepage, |
| + .writepages = exfat_writepages, |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) |
| + .sync_page = block_sync_page, |
| +#endif |
| + .write_begin = exfat_write_begin, |
| + .write_end = exfat_write_end, |
| + .direct_IO = exfat_direct_IO, |
| + .bmap = _exfat_bmap |
| +}; |
| + |
| +static inline unsigned long exfat_hash(loff_t i_pos) |
| +{ |
| + return hash_32(i_pos, EXFAT_HASH_BITS); |
| +} |
| + |
| +static 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_node *node; |
| + 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, node, head, i_hash_fat) { |
| + CHECK_ERR(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; |
| +} |
| + |
| +static void exfat_attach(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); |
| +} |
| + |
| +static void exfat_detach(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); |
| +} |
| + |
| +static int exfat_fill_inode(struct inode *inode, FILE_ID_T *fid) |
| +{ |
| + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); |
| + FS_INFO_T *p_fs = &(sbi->fs_info); |
| + DIR_ENTRY_T info; |
| + |
| + memcpy(&(EXFAT_I(inode)->fid), fid, sizeof(FILE_ID_T)); |
| + |
| + FsReadStat(inode, &info); |
| + |
| + EXFAT_I(inode)->i_pos = 0; |
| + EXFAT_I(inode)->target = NULL; |
| + inode->i_uid = sbi->options.fs_uid; |
| + inode->i_gid = sbi->options.fs_gid; |
| + inode->i_version++; |
| + inode->i_generation = get_seconds(); |
| + |
| + if (info.Attr & ATTR_SUBDIR) { |
| + inode->i_generation &= ~1; |
| + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); |
| + inode->i_op = &exfat_dir_inode_operations; |
| + inode->i_fop = &exfat_dir_operations; |
| + |
| + i_size_write(inode, info.Size); |
| + EXFAT_I(inode)->mmu_private = i_size_read(inode); |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) |
| + set_nlink(inode,info.NumSubdirs); |
| +#else |
| + inode->i_nlink = info.NumSubdirs; |
| +#endif |
| + } else if (info.Attr & ATTR_SYMLINK) { |
| + inode->i_generation |= 1; |
| + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); |
| + inode->i_op = &exfat_symlink_inode_operations; |
| + |
| + i_size_write(inode, info.Size); |
| + EXFAT_I(inode)->mmu_private = i_size_read(inode); |
| + } else { |
| + inode->i_generation |= 1; |
| + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); |
| + 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, info.Size); |
| + EXFAT_I(inode)->mmu_private = i_size_read(inode); |
| + } |
| + exfat_save_attr(inode, info.Attr); |
| + |
| + inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) |
| + & ~((loff_t)p_fs->cluster_size - 1)) >> 9; |
| + |
| + exfat_time_fat2unix(sbi, &inode->i_mtime, &info.ModifyTimestamp); |
| + exfat_time_fat2unix(sbi, &inode->i_ctime, &info.CreateTimestamp); |
| + exfat_time_fat2unix(sbi, &inode->i_atime, &info.AccessTimestamp); |
| + |
| + return 0; |
| +} |
| + |
| +static struct inode *exfat_build_inode(struct super_block *sb, |
| + FILE_ID_T *fid, 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->i_version = 1; |
| + err = exfat_fill_inode(inode, fid); |
| + if (err) { |
| + iput(inode); |
| + inode = ERR_PTR(err); |
| + goto out; |
| + } |
| + exfat_attach(inode, i_pos); |
| + insert_inode_hash(inode); |
| +out: |
| + return inode; |
| +} |
| + |
| +static int exfat_sync_inode(struct inode *inode) |
| +{ |
| + return exfat_write_inode(inode, NULL); |
| +} |
| + |
| +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; |
| + |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) |
| + init_rwsem(&ei->truncate_lock); |
| +#endif |
| + |
| + return &ei->vfs_inode; |
| +} |
| + |
| +static void exfat_destroy_inode(struct inode *inode) |
| +{ |
| + FREE(EXFAT_I(inode)->target); |
| + EXFAT_I(inode)->target = NULL; |
| + |
| + kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode)); |
| +} |
| + |
| +static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) |
| +{ |
| + struct super_block *sb = inode->i_sb; |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + DIR_ENTRY_T info; |
| + |
| + if (inode->i_ino == EXFAT_ROOT_INO) |
| + return 0; |
| + |
| + info.Attr = exfat_make_attr(inode); |
| + info.Size = i_size_read(inode); |
| + |
| + exfat_time_unix2fat(sbi, &inode->i_mtime, &info.ModifyTimestamp); |
| + exfat_time_unix2fat(sbi, &inode->i_ctime, &info.CreateTimestamp); |
| + exfat_time_unix2fat(sbi, &inode->i_atime, &info.AccessTimestamp); |
| + |
| + FsWriteStat(inode, &info); |
| + |
| + return 0; |
| +} |
| + |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) |
| +static void exfat_delete_inode(struct inode *inode) |
| +{ |
| + truncate_inode_pages(&inode->i_data, 0); |
| + clear_inode(inode); |
| +} |
| + |
| +static void exfat_clear_inode(struct inode *inode) |
| +{ |
| + exfat_detach(inode); |
| + remove_inode_hash(inode); |
| +} |
| +#else |
| +static void exfat_evict_inode(struct inode *inode) |
| +{ |
| + truncate_inode_pages(&inode->i_data, 0); |
| + |
| + if (!inode->i_nlink) |
| + i_size_write(inode, 0); |
| + invalidate_inode_buffers(inode); |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,00) |
| + end_writeback(inode); |
| +#else |
| + clear_inode(inode); |
| +#endif |
| + exfat_detach(inode); |
| + |
| + remove_inode_hash(inode); |
| +} |
| +#endif |
| + |
| + |
| +static void exfat_put_super(struct super_block *sb) |
| +{ |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + if (__is_sb_dirty(sb)) |
| + exfat_write_super(sb); |
| + |
| + FsUmountVol(sb); |
| + |
| + if (sbi->nls_disk) { |
| + unload_nls(sbi->nls_disk); |
| + sbi->nls_disk = NULL; |
| + sbi->options.codepage = exfat_default_codepage; |
| + } |
| + if (sbi->nls_io) { |
| + unload_nls(sbi->nls_io); |
| + sbi->nls_io = NULL; |
| + } |
| + if (sbi->options.iocharset != exfat_default_iocharset) { |
| + kfree(sbi->options.iocharset); |
| + sbi->options.iocharset = exfat_default_iocharset; |
| + } |
| + |
| + sb->s_fs_info = NULL; |
| + kfree(sbi); |
| +} |
| + |
| +static void exfat_write_super(struct super_block *sb) |
| +{ |
| + __lock_super(sb); |
| + |
| + __set_sb_clean(sb); |
| + |
| + if (!(sb->s_flags & MS_RDONLY)) |
| + FsSyncVol(sb, 1); |
| + |
| + __unlock_super(sb); |
| +} |
| + |
| +static int exfat_sync_fs(struct super_block *sb, int wait) |
| +{ |
| + int err = 0; |
| + |
| + if (__is_sb_dirty(sb)) { |
| + __lock_super(sb); |
| + __set_sb_clean(sb); |
| + err = FsSyncVol(sb, 1); |
| + __unlock_super(sb); |
| + } |
| + |
| + return err; |
| +} |
| + |
| +static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf) |
| +{ |
| + struct super_block *sb = dentry->d_sb; |
| + u64 id = huge_encode_dev(sb->s_bdev->bd_dev); |
| + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); |
| + VOL_INFO_T info; |
| + |
| + if (p_fs->used_clusters == (UINT32) ~0) { |
| + if (FFS_MEDIAERR == FsGetVolInfo(sb, &info)) |
| + return -EIO; |
| + |
| + } else { |
| + info.FatType = p_fs->vol_type; |
| + info.ClusterSize = p_fs->cluster_size; |
| + info.NumClusters = p_fs->num_clusters - 2; |
| + info.UsedClusters = p_fs->used_clusters; |
| + info.FreeClusters = info.NumClusters - info.UsedClusters; |
| + |
| + if (p_fs->dev_ejected) |
| + return -EIO; |
| + } |
| + |
| + buf->f_type = sb->s_magic; |
| + buf->f_bsize = info.ClusterSize; |
| + buf->f_blocks = info.NumClusters; |
| + buf->f_bfree = info.FreeClusters; |
| + buf->f_bavail = info.FreeClusters; |
| + buf->f_fsid.val[0] = (u32)id; |
| + buf->f_fsid.val[1] = (u32)(id >> 32); |
| + buf->f_namelen = 260; |
| + |
| + return 0; |
| +} |
| + |
| +static int exfat_remount(struct super_block *sb, int *flags, char *data) |
| +{ |
| + *flags |= MS_NODIRATIME; |
| + return 0; |
| +} |
| + |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) |
| +static int exfat_show_options(struct seq_file *m, struct dentry *root) |
| +{ |
| + struct exfat_sb_info *sbi = EXFAT_SB(root->d_sb); |
| +#else |
| +static int exfat_show_options(struct seq_file *m, struct vfsmount *mnt) |
| +{ |
| + struct exfat_sb_info *sbi = EXFAT_SB(mnt->mnt_sb); |
| +#endif |
| + struct exfat_mount_options *opts = &sbi->options; |
| + |
| + if (opts->fs_uid != 0) |
| + seq_printf(m, ",uid=%u", opts->fs_uid); |
| + if (opts->fs_gid != 0) |
| + seq_printf(m, ",gid=%u", opts->fs_gid); |
| + seq_printf(m, ",fmask=%04o", opts->fs_fmask); |
| + seq_printf(m, ",dmask=%04o", opts->fs_dmask); |
| + if (opts->allow_utime) |
| + seq_printf(m, ",allow_utime=%04o", opts->allow_utime); |
| + if (sbi->nls_disk) |
| + seq_printf(m, ",codepage=%s", sbi->nls_disk->charset); |
| + if (sbi->nls_io) |
| + seq_printf(m, ",iocharset=%s", sbi->nls_io->charset); |
| + seq_printf(m, ",namecase=%u", opts->casesensitive); |
| + 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 EXFAT_CONFIG_DISCARD |
| + if (opts->discard) |
| + seq_printf(m, ",discard"); |
| +#endif |
| + return 0; |
| +} |
| + |
| +const struct super_operations exfat_sops = { |
| + .alloc_inode = exfat_alloc_inode, |
| + .destroy_inode = exfat_destroy_inode, |
| + .write_inode = exfat_write_inode, |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) |
| + .delete_inode = exfat_delete_inode, |
| + .clear_inode = exfat_clear_inode, |
| +#else |
| + .evict_inode = exfat_evict_inode, |
| +#endif |
| + .put_super = exfat_put_super, |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,00) |
| + .write_super = exfat_write_super, |
| +#endif |
| + .sync_fs = exfat_sync_fs, |
| + .statfs = exfat_statfs, |
| + .remount_fs = exfat_remount, |
| + .show_options = exfat_show_options, |
| +}; |
| + |
| +enum { |
| + Opt_uid, |
| + Opt_gid, |
| + Opt_umask, |
| + Opt_dmask, |
| + Opt_fmask, |
| + Opt_allow_utime, |
| + Opt_codepage, |
| + Opt_charset, |
| + Opt_namecase, |
| + Opt_debug, |
| + Opt_err_cont, |
| + Opt_err_panic, |
| + Opt_err_ro, |
| + Opt_err, |
| +#if EXFAT_CONFIG_DISCARD |
| + Opt_discard, |
| +#endif |
| +}; |
| + |
| +static const match_table_t exfat_tokens = { |
| + {Opt_uid, "uid=%u"}, |
| + {Opt_gid, "gid=%u"}, |
| + {Opt_umask, "umask=%o"}, |
| + {Opt_dmask, "dmask=%o"}, |
| + {Opt_fmask, "fmask=%o"}, |
| + {Opt_allow_utime, "allow_utime=%o"}, |
| + {Opt_codepage, "codepage=%u"}, |
| + {Opt_charset, "iocharset=%s"}, |
| + {Opt_namecase, "namecase=%u"}, |
| + {Opt_debug, "debug"}, |
| + {Opt_err_cont, "errors=continue"}, |
| + {Opt_err_panic, "errors=panic"}, |
| + {Opt_err_ro, "errors=remount-ro"}, |
| +#if EXFAT_CONFIG_DISCARD |
| + {Opt_discard, "discard"}, |
| +#endif |
| + {Opt_err, NULL} |
| +}; |
| + |
| +static int parse_options(char *options, int silent, int *debug, |
| + struct exfat_mount_options *opts) |
| +{ |
| + char *p; |
| + substring_t args[MAX_OPT_ARGS]; |
| + int option; |
| + char *iocharset; |
| + |
| + opts->fs_uid = current_uid(); |
| + opts->fs_gid = current_gid(); |
| + opts->fs_fmask = opts->fs_dmask = current->fs->umask; |
| + opts->allow_utime = (unsigned short) -1; |
| + opts->codepage = exfat_default_codepage; |
| + opts->iocharset = exfat_default_iocharset; |
| + opts->casesensitive = 0; |
| + opts->errors = EXFAT_ERRORS_RO; |
| +#if EXFAT_CONFIG_DISCARD |
| + opts->discard = 0; |
| +#endif |
| + *debug = 0; |
| + |
| + if (!options) |
| + goto out; |
| + |
| + while ((p = strsep(&options, ",")) != NULL) { |
| + int token; |
| + if (!*p) |
| + continue; |
| + |
| + token = match_token(p, exfat_tokens, args); |
| + switch (token) { |
| + case Opt_uid: |
| + if (match_int(&args[0], &option)) |
| + return 0; |
| + opts->fs_uid = option; |
| + break; |
| + case Opt_gid: |
| + if (match_int(&args[0], &option)) |
| + return 0; |
| + opts->fs_gid = option; |
| + break; |
| + case Opt_umask: |
| + case Opt_dmask: |
| + case Opt_fmask: |
| + if (match_octal(&args[0], &option)) |
| + return 0; |
| + if (token != Opt_dmask) |
| + opts->fs_fmask = option; |
| + if (token != Opt_fmask) |
| + opts->fs_dmask = option; |
| + break; |
| + case Opt_allow_utime: |
| + if (match_octal(&args[0], &option)) |
| + return 0; |
| + opts->allow_utime = option & (S_IWGRP | S_IWOTH); |
| + break; |
| + case Opt_codepage: |
| + if (match_int(&args[0], &option)) |
| + return 0; |
| + opts->codepage = option; |
| + break; |
| + case Opt_charset: |
| + if (opts->iocharset != exfat_default_iocharset) |
| + kfree(opts->iocharset); |
| + iocharset = match_strdup(&args[0]); |
| + if (!iocharset) |
| + return -ENOMEM; |
| + opts->iocharset = iocharset; |
| + break; |
| + case Opt_namecase: |
| + if (match_int(&args[0], &option)) |
| + return 0; |
| + opts->casesensitive = option; |
| + break; |
| + case Opt_err_cont: |
| + opts->errors = EXFAT_ERRORS_CONT; |
| + break; |
| + case Opt_err_panic: |
| + opts->errors = EXFAT_ERRORS_PANIC; |
| + break; |
| + case Opt_err_ro: |
| + opts->errors = EXFAT_ERRORS_RO; |
| + break; |
| + case Opt_debug: |
| + *debug = 1; |
| + break; |
| +#if EXFAT_CONFIG_DISCARD |
| + case Opt_discard: |
| + opts->discard = 1; |
| + break; |
| +#endif |
| + default: |
| + if (!silent) { |
| + printk(KERN_ERR "[EXFAT] Unrecognized mount option %s or missing value\n", p); |
| + } |
| + return -EINVAL; |
| + } |
| + } |
| + |
| +out: |
| + if (opts->allow_utime == (unsigned short) -1) |
| + opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH); |
| + |
| + 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 timespec ts; |
| + FS_INFO_T *p_fs = &(sbi->fs_info); |
| + DIR_ENTRY_T info; |
| + |
| + ts = CURRENT_TIME_SEC; |
| + |
| + EXFAT_I(inode)->fid.dir.dir = p_fs->root_dir; |
| + EXFAT_I(inode)->fid.dir.flags = 0x01; |
| + EXFAT_I(inode)->fid.entry = -1; |
| + EXFAT_I(inode)->fid.start_clu = p_fs->root_dir; |
| + EXFAT_I(inode)->fid.flags = 0x01; |
| + EXFAT_I(inode)->fid.type = TYPE_DIR; |
| + EXFAT_I(inode)->fid.rwoffset = 0; |
| + EXFAT_I(inode)->fid.hint_last_off = -1; |
| + |
| + EXFAT_I(inode)->target = NULL; |
| + |
| + FsReadStat(inode, &info); |
| + |
| + inode->i_uid = sbi->options.fs_uid; |
| + inode->i_gid = sbi->options.fs_gid; |
| + inode->i_version++; |
| + inode->i_generation = 0; |
| + inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, S_IRWXUGO); |
| + inode->i_op = &exfat_dir_inode_operations; |
| + inode->i_fop = &exfat_dir_operations; |
| + |
| + i_size_write(inode, info.Size); |
| + inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) |
| + & ~((loff_t)p_fs->cluster_size - 1)) >> 9; |
| + EXFAT_I(inode)->i_pos = ((loff_t) p_fs->root_dir << 32) | 0xffffffff; |
| + EXFAT_I(inode)->mmu_private = i_size_read(inode); |
| + |
| + exfat_save_attr(inode, ATTR_SUBDIR); |
| + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) |
| + set_nlink(inode,info.NumSubdirs + 2); |
| +#else |
| + inode->i_nlink = info.NumSubdirs + 2; |
| +#endif |
| + |
| + return 0; |
| +} |
| + |
| +static int exfat_fill_super(struct super_block *sb, void *data, int silent) |
| +{ |
| + struct inode *root_inode = NULL; |
| + struct exfat_sb_info *sbi; |
| + int debug, ret; |
| + long error; |
| + char buf[50]; |
| + |
| + sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL); |
| + if (!sbi) |
| + return -ENOMEM; |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) |
| + mutex_init(&sbi->s_lock); |
| +#endif |
| + sb->s_fs_info = sbi; |
| + |
| + sb->s_flags |= MS_NODIRATIME; |
| + sb->s_magic = EXFAT_SUPER_MAGIC; |
| + sb->s_op = &exfat_sops; |
| + |
| + error = parse_options(data, silent, &debug, &sbi->options); |
| + if (error) |
| + goto out_fail; |
| + |
| + error = -EIO; |
| + sb_min_blocksize(sb, 512); |
| + sb->s_maxbytes = 0x7fffffffffffffffLL; |
| + |
| + ret = FsMountVol(sb); |
| + if (ret) { |
| + if (!silent) |
| + printk(KERN_ERR "[EXFAT] FsMountVol failed\n"); |
| + |
| + goto out_fail; |
| + } |
| + |
| + exfat_hash_init(sb); |
| + |
| + error = -EINVAL; |
| + sprintf(buf, "cp%d", sbi->options.codepage); |
| + sbi->nls_disk = load_nls(buf); |
| + if (!sbi->nls_disk) { |
| + printk(KERN_ERR "[EXFAT] Codepage %s not found\n", buf); |
| + goto out_fail2; |
| + } |
| + |
| + sbi->nls_io = load_nls(sbi->options.iocharset); |
| + if (!sbi->nls_io) { |
| + printk(KERN_ERR "[EXFAT] IO charset %s not found\n", |
| + sbi->options.iocharset); |
| + goto out_fail2; |
| + } |
| + |
| + error = -ENOMEM; |
| + root_inode = new_inode(sb); |
| + if (!root_inode) |
| + goto out_fail2; |
| + root_inode->i_ino = EXFAT_ROOT_INO; |
| + root_inode->i_version = 1; |
| + error = exfat_read_root(root_inode); |
| + if (error < 0) |
| + goto out_fail2; |
| + error = -ENOMEM; |
| + exfat_attach(root_inode, EXFAT_I(root_inode)->i_pos); |
| + insert_inode_hash(root_inode); |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) |
| + sb->s_root = d_make_root(root_inode); |
| +#else |
| + sb->s_root = d_alloc_root(root_inode); |
| +#endif |
| + if (!sb->s_root) { |
| + printk(KERN_ERR "[EXFAT] Getting the root inode failed\n"); |
| + goto out_fail2; |
| + } |
| + |
| + return 0; |
| + |
| +out_fail2: |
| + FsUmountVol(sb); |
| +out_fail: |
| + if (root_inode) |
| + iput(root_inode); |
| + if (sbi->nls_io) |
| + unload_nls(sbi->nls_io); |
| + if (sbi->nls_disk) |
| + unload_nls(sbi->nls_disk); |
| + if (sbi->options.iocharset != exfat_default_iocharset) |
| + kfree(sbi->options.iocharset); |
| + sb->s_fs_info = NULL; |
| + kfree(sbi); |
| + return error; |
| +} |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) |
| +static int exfat_get_sb(struct file_system_type *fs_type, |
| + int flags, const char *dev_name, |
| + void *data, struct vfsmount *mnt) |
| +{ |
| + return get_sb_bdev(fs_type, flags, dev_name, data, exfat_fill_super, mnt); |
| +} |
| +#else |
| +static struct dentry *exfat_fs_mount(struct file_system_type *fs_type, |
| + int flags, const char *dev_name, |
| + void *data) { |
| + return mount_bdev(fs_type, flags, dev_name, data, exfat_fill_super); |
| +} |
| +#endif |
| + |
| +static void init_once(void *foo) |
| +{ |
| + struct exfat_inode_info *ei = (struct exfat_inode_info *)foo; |
| + |
| + INIT_HLIST_NODE(&ei->i_hash_fat); |
| + inode_init_once(&ei->vfs_inode); |
| +} |
| + |
| +static int __init exfat_init_inodecache(void) |
| +{ |
| + exfat_inode_cachep = kmem_cache_create("exfat_inode_cache", |
| + sizeof(struct exfat_inode_info), |
| + 0, (SLAB_RECLAIM_ACCOUNT| |
| + SLAB_MEM_SPREAD), |
| + init_once); |
| + if (exfat_inode_cachep == NULL) |
| + return -ENOMEM; |
| + return 0; |
| +} |
| + |
| +static void __exit exfat_destroy_inodecache(void) |
| +{ |
| + kmem_cache_destroy(exfat_inode_cachep); |
| +} |
| + |
| +#if EXFAT_CONFIG_KERNEL_DEBUG |
| +static void exfat_debug_kill_sb(struct super_block *sb) |
| +{ |
| + struct exfat_sb_info *sbi = EXFAT_SB(sb); |
| + struct block_device *bdev = sb->s_bdev; |
| + |
| + long flags; |
| + |
| + if (sbi) { |
| + flags = sbi->debug_flags; |
| + |
| + if (flags & EXFAT_DEBUGFLAGS_INVALID_UMOUNT) { |
| + FsReleaseCache(sb); |
| + invalidate_bdev(bdev); |
| + } |
| + } |
| + |
| + kill_block_super(sb); |
| +} |
| +#endif |
| + |
| +static struct file_system_type exfat_fs_type = { |
| + .owner = THIS_MODULE, |
| + .name = "exfat", |
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) |
| + .get_sb = exfat_get_sb, |
| +#else |
| + .mount = exfat_fs_mount, |
| +#endif |
| +#if EXFAT_CONFIG_KERNEL_DEBUG |
| + .kill_sb = exfat_debug_kill_sb, |
| +#else |
| + .kill_sb = kill_block_super, |
| +#endif |
| + .fs_flags = FS_REQUIRES_DEV, |
| +}; |
| + |
| +static int __init init_exfat_fs(void) |
| +{ |
| + int err; |
| + |
| + printk(KERN_INFO "exFAT: FS Version %s\n", EXFAT_VERSION); |
| + |
| + err = exfat_init_inodecache(); |
| + if (err) return err; |
| + |
| + return register_filesystem(&exfat_fs_type); |
| +} |
| + |
| +static void __exit exit_exfat_fs(void) |
| +{ |
| + exfat_destroy_inodecache(); |
| + unregister_filesystem(&exfat_fs_type); |
| +} |
| + |
| +module_init(init_exfat_fs); |
| +module_exit(exit_exfat_fs); |
| + |
| +MODULE_LICENSE("GPL"); |
| diff --git a/drivers/staging/exfat/exfat_super.h b/drivers/staging/exfat/exfat_super.h |
| new file mode 100644 |
| index 00000000..22f4ee26 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_super.h |
| @@ -0,0 +1,149 @@ |
| +/* Some of the source code in this file came from "linux/fs/fat/fat.h". */ |
| + |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#ifndef _EXFAT_LINUX_H |
| +#define _EXFAT_LINUX_H |
| + |
| +#include <linux/buffer_head.h> |
| +#include <linux/string.h> |
| +#include <linux/nls.h> |
| +#include <linux/fs.h> |
| +#include <linux/mutex.h> |
| +#include <linux/swap.h> |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| +#include "exfat_data.h" |
| +#include "exfat_oal.h" |
| + |
| +#include "exfat_blkdev.h" |
| +#include "exfat_cache.h" |
| +#include "exfat_part.h" |
| +#include "exfat_nls.h" |
| +#include "exfat_api.h" |
| +#include "exfat.h" |
| + |
| +#define EXFAT_ERRORS_CONT 1 |
| +#define EXFAT_ERRORS_PANIC 2 |
| +#define EXFAT_ERRORS_RO 3 |
| + |
| +#define EXFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32) |
| + |
| +struct exfat_mount_options { |
| + uid_t fs_uid; |
| + gid_t fs_gid; |
| + unsigned short fs_fmask; |
| + unsigned short fs_dmask; |
| + unsigned short allow_utime; |
| + unsigned short codepage; |
| + char *iocharset; |
| + unsigned char casesensitive; |
| + unsigned char errors; |
| +#if EXFAT_CONFIG_DISCARD |
| + unsigned char discard; |
| +#endif |
| +}; |
| + |
| +#define EXFAT_HASH_BITS 8 |
| +#define EXFAT_HASH_SIZE (1UL << EXFAT_HASH_BITS) |
| + |
| +struct exfat_sb_info { |
| + FS_INFO_T fs_info; |
| + BD_INFO_T bd_info; |
| + |
| + struct exfat_mount_options options; |
| + |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) |
| + int s_dirt; |
| + struct mutex s_lock; |
| +#endif |
| + struct nls_table *nls_disk; |
| + struct nls_table *nls_io; |
| + |
| + struct inode *fat_inode; |
| + |
| + spinlock_t inode_hash_lock; |
| + struct hlist_head inode_hashtable[EXFAT_HASH_SIZE]; |
| +#if EXFAT_CONFIG_KERNEL_DEBUG |
| + long debug_flags; |
| +#endif |
| +}; |
| + |
| +struct exfat_inode_info { |
| + FILE_ID_T fid; |
| + char *target; |
| + loff_t mmu_private; |
| + loff_t i_pos; |
| + struct hlist_node i_hash_fat; |
| +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) |
| + struct rw_semaphore truncate_lock; |
| +#endif |
| + struct inode vfs_inode; |
| +}; |
| + |
| +#define EXFAT_SB(sb) ((struct exfat_sb_info *)((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); |
| +} |
| + |
| +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) & S_IWUGO) |
| + return 1; |
| + return 0; |
| +} |
| + |
| +static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi, |
| + u32 attr, mode_t mode) |
| +{ |
| + if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR)) |
| + mode &= ~S_IWUGO; |
| + |
| + if (attr & ATTR_SUBDIR) |
| + return (mode & ~sbi->options.fs_dmask) | S_IFDIR; |
| + else if (attr & ATTR_SYMLINK) |
| + return (mode & ~sbi->options.fs_dmask) | S_IFLNK; |
| + else |
| + return (mode & ~sbi->options.fs_fmask) | S_IFREG; |
| +} |
| + |
| +static inline u32 exfat_make_attr(struct inode *inode) |
| +{ |
| + if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO)) |
| + return ((EXFAT_I(inode)->fid.attr) | ATTR_READONLY); |
| + else |
| + return (EXFAT_I(inode)->fid.attr); |
| +} |
| + |
| +static inline void exfat_save_attr(struct inode *inode, u32 attr) |
| +{ |
| + if (exfat_mode_can_hold_ro(inode)) |
| + EXFAT_I(inode)->fid.attr = attr & ATTR_RWMASK; |
| + else |
| + EXFAT_I(inode)->fid.attr = attr & (ATTR_RWMASK | ATTR_READONLY); |
| +} |
| + |
| +#endif |
| diff --git a/drivers/staging/exfat/exfat_upcase.c b/drivers/staging/exfat/exfat_upcase.c |
| new file mode 100644 |
| index 00000000..7378a157 |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_upcase.c |
| @@ -0,0 +1,390 @@ |
| +/* |
| + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. |
| + * |
| + * This program is free software; you can redistribute it and/or |
| + * modify it under the terms of the GNU General Public License |
| + * as published by the Free Software Foundation; either version 2 |
| + * of the License, or (at your option) any later version. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| + */ |
| + |
| +#include "exfat_config.h" |
| +#include "exfat_global.h" |
| + |
| +#include "exfat_nls.h" |
| + |
| +UINT8 uni_upcase[NUM_UPCASE<<1] = { |
| + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, |
| + 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0F, 0x00, |
| + 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, |
| + 0x18, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x1F, 0x00, |
| + 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, |
| + 0x28, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B, 0x00, 0x2C, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x2F, 0x00, |
| + 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, |
| + 0x38, 0x00, 0x39, 0x00, 0x3A, 0x00, 0x3B, 0x00, 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F, 0x00, |
| + 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, |
| + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, |
| + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, |
| + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x5B, 0x00, 0x5C, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x5F, 0x00, |
| + 0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, |
| + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, |
| + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, |
| + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x7B, 0x00, 0x7C, 0x00, 0x7D, 0x00, 0x7E, 0x00, 0x7F, 0x00, |
| + 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, |
| + 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00, 0x8C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00, |
| + 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, |
| + 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00, 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00, |
| + 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, 0x00, |
| + 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, 0x00, 0xAC, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAF, 0x00, |
| + 0xB0, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB7, 0x00, |
| + 0xB8, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBB, 0x00, 0xBC, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBF, 0x00, |
| + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, |
| + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, |
| + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD7, 0x00, |
| + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0xDF, 0x00, |
| + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, |
| + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, |
| + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xF7, 0x00, |
| + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0x78, 0x01, |
| + 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01, |
| + 0x08, 0x01, 0x08, 0x01, 0x0A, 0x01, 0x0A, 0x01, 0x0C, 0x01, 0x0C, 0x01, 0x0E, 0x01, 0x0E, 0x01, |
| + 0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01, 0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01, |
| + 0x18, 0x01, 0x18, 0x01, 0x1A, 0x01, 0x1A, 0x01, 0x1C, 0x01, 0x1C, 0x01, 0x1E, 0x01, 0x1E, 0x01, |
| + 0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01, 0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01, |
| + 0x28, 0x01, 0x28, 0x01, 0x2A, 0x01, 0x2A, 0x01, 0x2C, 0x01, 0x2C, 0x01, 0x2E, 0x01, 0x2E, 0x01, |
| + 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01, 0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01, |
| + 0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3B, 0x01, 0x3B, 0x01, 0x3D, 0x01, 0x3D, 0x01, 0x3F, 0x01, |
| + 0x3F, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01, 0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01, |
| + 0x47, 0x01, 0x49, 0x01, 0x4A, 0x01, 0x4A, 0x01, 0x4C, 0x01, 0x4C, 0x01, 0x4E, 0x01, 0x4E, 0x01, |
| + 0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01, 0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01, |
| + 0x58, 0x01, 0x58, 0x01, 0x5A, 0x01, 0x5A, 0x01, 0x5C, 0x01, 0x5C, 0x01, 0x5E, 0x01, 0x5E, 0x01, |
| + 0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01, 0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01, |
| + 0x68, 0x01, 0x68, 0x01, 0x6A, 0x01, 0x6A, 0x01, 0x6C, 0x01, 0x6C, 0x01, 0x6E, 0x01, 0x6E, 0x01, |
| + 0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01, 0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01, |
| + 0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7B, 0x01, 0x7B, 0x01, 0x7D, 0x01, 0x7D, 0x01, 0x7F, 0x01, |
| + 0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01, 0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01, |
| + 0x87, 0x01, 0x89, 0x01, 0x8A, 0x01, 0x8B, 0x01, 0x8B, 0x01, 0x8D, 0x01, 0x8E, 0x01, 0x8F, 0x01, |
| + 0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01, 0x94, 0x01, 0xF6, 0x01, 0x96, 0x01, 0x97, 0x01, |
| + 0x98, 0x01, 0x98, 0x01, 0x3D, 0x02, 0x9B, 0x01, 0x9C, 0x01, 0x9D, 0x01, 0x20, 0x02, 0x9F, 0x01, |
| + 0xA0, 0x01, 0xA0, 0x01, 0xA2, 0x01, 0xA2, 0x01, 0xA4, 0x01, 0xA4, 0x01, 0xA6, 0x01, 0xA7, 0x01, |
| + 0xA7, 0x01, 0xA9, 0x01, 0xAA, 0x01, 0xAB, 0x01, 0xAC, 0x01, 0xAC, 0x01, 0xAE, 0x01, 0xAF, 0x01, |
| + 0xAF, 0x01, 0xB1, 0x01, 0xB2, 0x01, 0xB3, 0x01, 0xB3, 0x01, 0xB5, 0x01, 0xB5, 0x01, 0xB7, 0x01, |
| + 0xB8, 0x01, 0xB8, 0x01, 0xBA, 0x01, 0xBB, 0x01, 0xBC, 0x01, 0xBC, 0x01, 0xBE, 0x01, 0xF7, 0x01, |
| + 0xC0, 0x01, 0xC1, 0x01, 0xC2, 0x01, 0xC3, 0x01, 0xC4, 0x01, 0xC5, 0x01, 0xC4, 0x01, 0xC7, 0x01, |
| + 0xC8, 0x01, 0xC7, 0x01, 0xCA, 0x01, 0xCB, 0x01, 0xCA, 0x01, 0xCD, 0x01, 0xCD, 0x01, 0xCF, 0x01, |
| + 0xCF, 0x01, 0xD1, 0x01, 0xD1, 0x01, 0xD3, 0x01, 0xD3, 0x01, 0xD5, 0x01, 0xD5, 0x01, 0xD7, 0x01, |
| + 0xD7, 0x01, 0xD9, 0x01, 0xD9, 0x01, 0xDB, 0x01, 0xDB, 0x01, 0x8E, 0x01, 0xDE, 0x01, 0xDE, 0x01, |
| + 0xE0, 0x01, 0xE0, 0x01, 0xE2, 0x01, 0xE2, 0x01, 0xE4, 0x01, 0xE4, 0x01, 0xE6, 0x01, 0xE6, 0x01, |
| + 0xE8, 0x01, 0xE8, 0x01, 0xEA, 0x01, 0xEA, 0x01, 0xEC, 0x01, 0xEC, 0x01, 0xEE, 0x01, 0xEE, 0x01, |
| + 0xF0, 0x01, 0xF1, 0x01, 0xF2, 0x01, 0xF1, 0x01, 0xF4, 0x01, 0xF4, 0x01, 0xF6, 0x01, 0xF7, 0x01, |
| + 0xF8, 0x01, 0xF8, 0x01, 0xFA, 0x01, 0xFA, 0x01, 0xFC, 0x01, 0xFC, 0x01, 0xFE, 0x01, 0xFE, 0x01, |
| + 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02, |
| + 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x0A, 0x02, 0x0C, 0x02, 0x0C, 0x02, 0x0E, 0x02, 0x0E, 0x02, |
| + 0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02, 0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02, |
| + 0x18, 0x02, 0x18, 0x02, 0x1A, 0x02, 0x1A, 0x02, 0x1C, 0x02, 0x1C, 0x02, 0x1E, 0x02, 0x1E, 0x02, |
| + 0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02, 0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02, |
| + 0x28, 0x02, 0x28, 0x02, 0x2A, 0x02, 0x2A, 0x02, 0x2C, 0x02, 0x2C, 0x02, 0x2E, 0x02, 0x2E, 0x02, |
| + 0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02, 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02, |
| + 0x38, 0x02, 0x39, 0x02, 0x65, 0x2C, 0x3B, 0x02, 0x3B, 0x02, 0x3D, 0x02, 0x66, 0x2C, 0x3F, 0x02, |
| + 0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02, 0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02, |
| + 0x48, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x4A, 0x02, 0x4C, 0x02, 0x4C, 0x02, 0x4E, 0x02, 0x4E, 0x02, |
| + 0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01, 0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8A, 0x01, |
| + 0x58, 0x02, 0x8F, 0x01, 0x5A, 0x02, 0x90, 0x01, 0x5C, 0x02, 0x5D, 0x02, 0x5E, 0x02, 0x5F, 0x02, |
| + 0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01, 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02, |
| + 0x97, 0x01, 0x96, 0x01, 0x6A, 0x02, 0x62, 0x2C, 0x6C, 0x02, 0x6D, 0x02, 0x6E, 0x02, 0x9C, 0x01, |
| + 0x70, 0x02, 0x71, 0x02, 0x9D, 0x01, 0x73, 0x02, 0x74, 0x02, 0x9F, 0x01, 0x76, 0x02, 0x77, 0x02, |
| + 0x78, 0x02, 0x79, 0x02, 0x7A, 0x02, 0x7B, 0x02, 0x7C, 0x02, 0x64, 0x2C, 0x7E, 0x02, 0x7F, 0x02, |
| + 0xA6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xA9, 0x01, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02, |
| + 0xAE, 0x01, 0x44, 0x02, 0xB1, 0x01, 0xB2, 0x01, 0x45, 0x02, 0x8D, 0x02, 0x8E, 0x02, 0x8F, 0x02, |
| + 0x90, 0x02, 0x91, 0x02, 0xB7, 0x01, 0x93, 0x02, 0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02, |
| + 0x98, 0x02, 0x99, 0x02, 0x9A, 0x02, 0x9B, 0x02, 0x9C, 0x02, 0x9D, 0x02, 0x9E, 0x02, 0x9F, 0x02, |
| + 0xA0, 0x02, 0xA1, 0x02, 0xA2, 0x02, 0xA3, 0x02, 0xA4, 0x02, 0xA5, 0x02, 0xA6, 0x02, 0xA7, 0x02, |
| + 0xA8, 0x02, 0xA9, 0x02, 0xAA, 0x02, 0xAB, 0x02, 0xAC, 0x02, 0xAD, 0x02, 0xAE, 0x02, 0xAF, 0x02, |
| + 0xB0, 0x02, 0xB1, 0x02, 0xB2, 0x02, 0xB3, 0x02, 0xB4, 0x02, 0xB5, 0x02, 0xB6, 0x02, 0xB7, 0x02, |
| + 0xB8, 0x02, 0xB9, 0x02, 0xBA, 0x02, 0xBB, 0x02, 0xBC, 0x02, 0xBD, 0x02, 0xBE, 0x02, 0xBF, 0x02, |
| + 0xC0, 0x02, 0xC1, 0x02, 0xC2, 0x02, 0xC3, 0x02, 0xC4, 0x02, 0xC5, 0x02, 0xC6, 0x02, 0xC7, 0x02, |
| + 0xC8, 0x02, 0xC9, 0x02, 0xCA, 0x02, 0xCB, 0x02, 0xCC, 0x02, 0xCD, 0x02, 0xCE, 0x02, 0xCF, 0x02, |
| + 0xD0, 0x02, 0xD1, 0x02, 0xD2, 0x02, 0xD3, 0x02, 0xD4, 0x02, 0xD5, 0x02, 0xD6, 0x02, 0xD7, 0x02, |
| + 0xD8, 0x02, 0xD9, 0x02, 0xDA, 0x02, 0xDB, 0x02, 0xDC, 0x02, 0xDD, 0x02, 0xDE, 0x02, 0xDF, 0x02, |
| + 0xE0, 0x02, 0xE1, 0x02, 0xE2, 0x02, 0xE3, 0x02, 0xE4, 0x02, 0xE5, 0x02, 0xE6, 0x02, 0xE7, 0x02, |
| + 0xE8, 0x02, 0xE9, 0x02, 0xEA, 0x02, 0xEB, 0x02, 0xEC, 0x02, 0xED, 0x02, 0xEE, 0x02, 0xEF, 0x02, |
| + 0xF0, 0x02, 0xF1, 0x02, 0xF2, 0x02, 0xF3, 0x02, 0xF4, 0x02, 0xF5, 0x02, 0xF6, 0x02, 0xF7, 0x02, |
| + 0xF8, 0x02, 0xF9, 0x02, 0xFA, 0x02, 0xFB, 0x02, 0xFC, 0x02, 0xFD, 0x02, 0xFE, 0x02, 0xFF, 0x02, |
| + 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03, |
| + 0x08, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0B, 0x03, 0x0C, 0x03, 0x0D, 0x03, 0x0E, 0x03, 0x0F, 0x03, |
| + 0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03, 0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03, |
| + 0x18, 0x03, 0x19, 0x03, 0x1A, 0x03, 0x1B, 0x03, 0x1C, 0x03, 0x1D, 0x03, 0x1E, 0x03, 0x1F, 0x03, |
| + 0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03, 0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03, |
| + 0x28, 0x03, 0x29, 0x03, 0x2A, 0x03, 0x2B, 0x03, 0x2C, 0x03, 0x2D, 0x03, 0x2E, 0x03, 0x2F, 0x03, |
| + 0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03, 0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03, |
| + 0x38, 0x03, 0x39, 0x03, 0x3A, 0x03, 0x3B, 0x03, 0x3C, 0x03, 0x3D, 0x03, 0x3E, 0x03, 0x3F, 0x03, |
| + 0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03, 0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03, |
| + 0x48, 0x03, 0x49, 0x03, 0x4A, 0x03, 0x4B, 0x03, 0x4C, 0x03, 0x4D, 0x03, 0x4E, 0x03, 0x4F, 0x03, |
| + 0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03, 0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03, |
| + 0x58, 0x03, 0x59, 0x03, 0x5A, 0x03, 0x5B, 0x03, 0x5C, 0x03, 0x5D, 0x03, 0x5E, 0x03, 0x5F, 0x03, |
| + 0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03, 0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03, |
| + 0x68, 0x03, 0x69, 0x03, 0x6A, 0x03, 0x6B, 0x03, 0x6C, 0x03, 0x6D, 0x03, 0x6E, 0x03, 0x6F, 0x03, |
| + 0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03, 0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03, |
| + 0x78, 0x03, 0x79, 0x03, 0x7A, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, 0x7E, 0x03, 0x7F, 0x03, |
| + 0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03, 0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03, |
| + 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, 0x8B, 0x03, 0x8C, 0x03, 0x8D, 0x03, 0x8E, 0x03, 0x8F, 0x03, |
| + 0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, |
| + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, |
| + 0xA0, 0x03, 0xA1, 0x03, 0xA2, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, |
| + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, |
| + 0xB0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, |
| + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, |
| + 0xA0, 0x03, 0xA1, 0x03, 0xA3, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, |
| + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x8C, 0x03, 0x8E, 0x03, 0x8F, 0x03, 0xCF, 0x03, |
| + 0xD0, 0x03, 0xD1, 0x03, 0xD2, 0x03, 0xD3, 0x03, 0xD4, 0x03, 0xD5, 0x03, 0xD6, 0x03, 0xD7, 0x03, |
| + 0xD8, 0x03, 0xD8, 0x03, 0xDA, 0x03, 0xDA, 0x03, 0xDC, 0x03, 0xDC, 0x03, 0xDE, 0x03, 0xDE, 0x03, |
| + 0xE0, 0x03, 0xE0, 0x03, 0xE2, 0x03, 0xE2, 0x03, 0xE4, 0x03, 0xE4, 0x03, 0xE6, 0x03, 0xE6, 0x03, |
| + 0xE8, 0x03, 0xE8, 0x03, 0xEA, 0x03, 0xEA, 0x03, 0xEC, 0x03, 0xEC, 0x03, 0xEE, 0x03, 0xEE, 0x03, |
| + 0xF0, 0x03, 0xF1, 0x03, 0xF9, 0x03, 0xF3, 0x03, 0xF4, 0x03, 0xF5, 0x03, 0xF6, 0x03, 0xF7, 0x03, |
| + 0xF7, 0x03, 0xF9, 0x03, 0xFA, 0x03, 0xFA, 0x03, 0xFC, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, |
| + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, |
| + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, |
| + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, |
| + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, |
| + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, |
| + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, |
| + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, |
| + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, |
| + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, |
| + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, |
| + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, |
| + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, |
| + 0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04, 0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04, |
| + 0x68, 0x04, 0x68, 0x04, 0x6A, 0x04, 0x6A, 0x04, 0x6C, 0x04, 0x6C, 0x04, 0x6E, 0x04, 0x6E, 0x04, |
| + 0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04, 0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04, |
| + 0x78, 0x04, 0x78, 0x04, 0x7A, 0x04, 0x7A, 0x04, 0x7C, 0x04, 0x7C, 0x04, 0x7E, 0x04, 0x7E, 0x04, |
| + 0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04, 0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04, |
| + 0x88, 0x04, 0x89, 0x04, 0x8A, 0x04, 0x8A, 0x04, 0x8C, 0x04, 0x8C, 0x04, 0x8E, 0x04, 0x8E, 0x04, |
| + 0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04, 0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04, |
| + 0x98, 0x04, 0x98, 0x04, 0x9A, 0x04, 0x9A, 0x04, 0x9C, 0x04, 0x9C, 0x04, 0x9E, 0x04, 0x9E, 0x04, |
| + 0xA0, 0x04, 0xA0, 0x04, 0xA2, 0x04, 0xA2, 0x04, 0xA4, 0x04, 0xA4, 0x04, 0xA6, 0x04, 0xA6, 0x04, |
| + 0xA8, 0x04, 0xA8, 0x04, 0xAA, 0x04, 0xAA, 0x04, 0xAC, 0x04, 0xAC, 0x04, 0xAE, 0x04, 0xAE, 0x04, |
| + 0xB0, 0x04, 0xB0, 0x04, 0xB2, 0x04, 0xB2, 0x04, 0xB4, 0x04, 0xB4, 0x04, 0xB6, 0x04, 0xB6, 0x04, |
| + 0xB8, 0x04, 0xB8, 0x04, 0xBA, 0x04, 0xBA, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBE, 0x04, 0xBE, 0x04, |
| + 0xC0, 0x04, 0xC1, 0x04, 0xC1, 0x04, 0xC3, 0x04, 0xC3, 0x04, 0xC5, 0x04, 0xC5, 0x04, 0xC7, 0x04, |
| + 0xC7, 0x04, 0xC9, 0x04, 0xC9, 0x04, 0xCB, 0x04, 0xCB, 0x04, 0xCD, 0x04, 0xCD, 0x04, 0xC0, 0x04, |
| + 0xD0, 0x04, 0xD0, 0x04, 0xD2, 0x04, 0xD2, 0x04, 0xD4, 0x04, 0xD4, 0x04, 0xD6, 0x04, 0xD6, 0x04, |
| + 0xD8, 0x04, 0xD8, 0x04, 0xDA, 0x04, 0xDA, 0x04, 0xDC, 0x04, 0xDC, 0x04, 0xDE, 0x04, 0xDE, 0x04, |
| + 0xE0, 0x04, 0xE0, 0x04, 0xE2, 0x04, 0xE2, 0x04, 0xE4, 0x04, 0xE4, 0x04, 0xE6, 0x04, 0xE6, 0x04, |
| + 0xE8, 0x04, 0xE8, 0x04, 0xEA, 0x04, 0xEA, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEE, 0x04, 0xEE, 0x04, |
| + 0xF0, 0x04, 0xF0, 0x04, 0xF2, 0x04, 0xF2, 0x04, 0xF4, 0x04, 0xF4, 0x04, 0xF6, 0x04, 0xF6, 0x04, |
| + 0xF8, 0x04, 0xF8, 0x04, 0xFA, 0x04, 0xFA, 0x04, 0xFC, 0x04, 0xFC, 0x04, 0xFE, 0x04, 0xFE, 0x04, |
| + 0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, |
| + 0x08, 0x05, 0x08, 0x05, 0x0A, 0x05, 0x0A, 0x05, 0x0C, 0x05, 0x0C, 0x05, 0x0E, 0x05, 0x0E, 0x05, |
| + 0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05, 0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05, |
| + 0x18, 0x05, 0x19, 0x05, 0x1A, 0x05, 0x1B, 0x05, 0x1C, 0x05, 0x1D, 0x05, 0x1E, 0x05, 0x1F, 0x05, |
| + 0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05, 0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05, |
| + 0x28, 0x05, 0x29, 0x05, 0x2A, 0x05, 0x2B, 0x05, 0x2C, 0x05, 0x2D, 0x05, 0x2E, 0x05, 0x2F, 0x05, |
| + 0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, |
| + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, |
| + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, |
| + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, |
| + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05, |
| + 0x58, 0x05, 0x59, 0x05, 0x5A, 0x05, 0x5B, 0x05, 0x5C, 0x05, 0x5D, 0x05, 0x5E, 0x05, 0x5F, 0x05, |
| + 0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, |
| + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, |
| + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, |
| + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, |
| + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xFF, 0xFF, |
| + 0xF6, 0x17, 0x63, 0x2C, 0x7E, 0x1D, 0x7F, 0x1D, 0x80, 0x1D, 0x81, 0x1D, 0x82, 0x1D, 0x83, 0x1D, |
| + 0x84, 0x1D, 0x85, 0x1D, 0x86, 0x1D, 0x87, 0x1D, 0x88, 0x1D, 0x89, 0x1D, 0x8A, 0x1D, 0x8B, 0x1D, |
| + 0x8C, 0x1D, 0x8D, 0x1D, 0x8E, 0x1D, 0x8F, 0x1D, 0x90, 0x1D, 0x91, 0x1D, 0x92, 0x1D, 0x93, 0x1D, |
| + 0x94, 0x1D, 0x95, 0x1D, 0x96, 0x1D, 0x97, 0x1D, 0x98, 0x1D, 0x99, 0x1D, 0x9A, 0x1D, 0x9B, 0x1D, |
| + 0x9C, 0x1D, 0x9D, 0x1D, 0x9E, 0x1D, 0x9F, 0x1D, 0xA0, 0x1D, 0xA1, 0x1D, 0xA2, 0x1D, 0xA3, 0x1D, |
| + 0xA4, 0x1D, 0xA5, 0x1D, 0xA6, 0x1D, 0xA7, 0x1D, 0xA8, 0x1D, 0xA9, 0x1D, 0xAA, 0x1D, 0xAB, 0x1D, |
| + 0xAC, 0x1D, 0xAD, 0x1D, 0xAE, 0x1D, 0xAF, 0x1D, 0xB0, 0x1D, 0xB1, 0x1D, 0xB2, 0x1D, 0xB3, 0x1D, |
| + 0xB4, 0x1D, 0xB5, 0x1D, 0xB6, 0x1D, 0xB7, 0x1D, 0xB8, 0x1D, 0xB9, 0x1D, 0xBA, 0x1D, 0xBB, 0x1D, |
| + 0xBC, 0x1D, 0xBD, 0x1D, 0xBE, 0x1D, 0xBF, 0x1D, 0xC0, 0x1D, 0xC1, 0x1D, 0xC2, 0x1D, 0xC3, 0x1D, |
| + 0xC4, 0x1D, 0xC5, 0x1D, 0xC6, 0x1D, 0xC7, 0x1D, 0xC8, 0x1D, 0xC9, 0x1D, 0xCA, 0x1D, 0xCB, 0x1D, |
| + 0xCC, 0x1D, 0xCD, 0x1D, 0xCE, 0x1D, 0xCF, 0x1D, 0xD0, 0x1D, 0xD1, 0x1D, 0xD2, 0x1D, 0xD3, 0x1D, |
| + 0xD4, 0x1D, 0xD5, 0x1D, 0xD6, 0x1D, 0xD7, 0x1D, 0xD8, 0x1D, 0xD9, 0x1D, 0xDA, 0x1D, 0xDB, 0x1D, |
| + 0xDC, 0x1D, 0xDD, 0x1D, 0xDE, 0x1D, 0xDF, 0x1D, 0xE0, 0x1D, 0xE1, 0x1D, 0xE2, 0x1D, 0xE3, 0x1D, |
| + 0xE4, 0x1D, 0xE5, 0x1D, 0xE6, 0x1D, 0xE7, 0x1D, 0xE8, 0x1D, 0xE9, 0x1D, 0xEA, 0x1D, 0xEB, 0x1D, |
| + 0xEC, 0x1D, 0xED, 0x1D, 0xEE, 0x1D, 0xEF, 0x1D, 0xF0, 0x1D, 0xF1, 0x1D, 0xF2, 0x1D, 0xF3, 0x1D, |
| + 0xF4, 0x1D, 0xF5, 0x1D, 0xF6, 0x1D, 0xF7, 0x1D, 0xF8, 0x1D, 0xF9, 0x1D, 0xFA, 0x1D, 0xFB, 0x1D, |
| + 0xFC, 0x1D, 0xFD, 0x1D, 0xFE, 0x1D, 0xFF, 0x1D, 0x00, 0x1E, 0x00, 0x1E, 0x02, 0x1E, 0x02, 0x1E, |
| + 0x04, 0x1E, 0x04, 0x1E, 0x06, 0x1E, 0x06, 0x1E, 0x08, 0x1E, 0x08, 0x1E, 0x0A, 0x1E, 0x0A, 0x1E, |
| + 0x0C, 0x1E, 0x0C, 0x1E, 0x0E, 0x1E, 0x0E, 0x1E, 0x10, 0x1E, 0x10, 0x1E, 0x12, 0x1E, 0x12, 0x1E, |
| + 0x14, 0x1E, 0x14, 0x1E, 0x16, 0x1E, 0x16, 0x1E, 0x18, 0x1E, 0x18, 0x1E, 0x1A, 0x1E, 0x1A, 0x1E, |
| + 0x1C, 0x1E, 0x1C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x20, 0x1E, 0x20, 0x1E, 0x22, 0x1E, 0x22, 0x1E, |
| + 0x24, 0x1E, 0x24, 0x1E, 0x26, 0x1E, 0x26, 0x1E, 0x28, 0x1E, 0x28, 0x1E, 0x2A, 0x1E, 0x2A, 0x1E, |
| + 0x2C, 0x1E, 0x2C, 0x1E, 0x2E, 0x1E, 0x2E, 0x1E, 0x30, 0x1E, 0x30, 0x1E, 0x32, 0x1E, 0x32, 0x1E, |
| + 0x34, 0x1E, 0x34, 0x1E, 0x36, 0x1E, 0x36, 0x1E, 0x38, 0x1E, 0x38, 0x1E, 0x3A, 0x1E, 0x3A, 0x1E, |
| + 0x3C, 0x1E, 0x3C, 0x1E, 0x3E, 0x1E, 0x3E, 0x1E, 0x40, 0x1E, 0x40, 0x1E, 0x42, 0x1E, 0x42, 0x1E, |
| + 0x44, 0x1E, 0x44, 0x1E, 0x46, 0x1E, 0x46, 0x1E, 0x48, 0x1E, 0x48, 0x1E, 0x4A, 0x1E, 0x4A, 0x1E, |
| + 0x4C, 0x1E, 0x4C, 0x1E, 0x4E, 0x1E, 0x4E, 0x1E, 0x50, 0x1E, 0x50, 0x1E, 0x52, 0x1E, 0x52, 0x1E, |
| + 0x54, 0x1E, 0x54, 0x1E, 0x56, 0x1E, 0x56, 0x1E, 0x58, 0x1E, 0x58, 0x1E, 0x5A, 0x1E, 0x5A, 0x1E, |
| + 0x5C, 0x1E, 0x5C, 0x1E, 0x5E, 0x1E, 0x5E, 0x1E, 0x60, 0x1E, 0x60, 0x1E, 0x62, 0x1E, 0x62, 0x1E, |
| + 0x64, 0x1E, 0x64, 0x1E, 0x66, 0x1E, 0x66, 0x1E, 0x68, 0x1E, 0x68, 0x1E, 0x6A, 0x1E, 0x6A, 0x1E, |
| + 0x6C, 0x1E, 0x6C, 0x1E, 0x6E, 0x1E, 0x6E, 0x1E, 0x70, 0x1E, 0x70, 0x1E, 0x72, 0x1E, 0x72, 0x1E, |
| + 0x74, 0x1E, 0x74, 0x1E, 0x76, 0x1E, 0x76, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x7A, 0x1E, 0x7A, 0x1E, |
| + 0x7C, 0x1E, 0x7C, 0x1E, 0x7E, 0x1E, 0x7E, 0x1E, 0x80, 0x1E, 0x80, 0x1E, 0x82, 0x1E, 0x82, 0x1E, |
| + 0x84, 0x1E, 0x84, 0x1E, 0x86, 0x1E, 0x86, 0x1E, 0x88, 0x1E, 0x88, 0x1E, 0x8A, 0x1E, 0x8A, 0x1E, |
| + 0x8C, 0x1E, 0x8C, 0x1E, 0x8E, 0x1E, 0x8E, 0x1E, 0x90, 0x1E, 0x90, 0x1E, 0x92, 0x1E, 0x92, 0x1E, |
| + 0x94, 0x1E, 0x94, 0x1E, 0x96, 0x1E, 0x97, 0x1E, 0x98, 0x1E, 0x99, 0x1E, 0x9A, 0x1E, 0x9B, 0x1E, |
| + 0x9C, 0x1E, 0x9D, 0x1E, 0x9E, 0x1E, 0x9F, 0x1E, 0xA0, 0x1E, 0xA0, 0x1E, 0xA2, 0x1E, 0xA2, 0x1E, |
| + 0xA4, 0x1E, 0xA4, 0x1E, 0xA6, 0x1E, 0xA6, 0x1E, 0xA8, 0x1E, 0xA8, 0x1E, 0xAA, 0x1E, 0xAA, 0x1E, |
| + 0xAC, 0x1E, 0xAC, 0x1E, 0xAE, 0x1E, 0xAE, 0x1E, 0xB0, 0x1E, 0xB0, 0x1E, 0xB2, 0x1E, 0xB2, 0x1E, |
| + 0xB4, 0x1E, 0xB4, 0x1E, 0xB6, 0x1E, 0xB6, 0x1E, 0xB8, 0x1E, 0xB8, 0x1E, 0xBA, 0x1E, 0xBA, 0x1E, |
| + 0xBC, 0x1E, 0xBC, 0x1E, 0xBE, 0x1E, 0xBE, 0x1E, 0xC0, 0x1E, 0xC0, 0x1E, 0xC2, 0x1E, 0xC2, 0x1E, |
| + 0xC4, 0x1E, 0xC4, 0x1E, 0xC6, 0x1E, 0xC6, 0x1E, 0xC8, 0x1E, 0xC8, 0x1E, 0xCA, 0x1E, 0xCA, 0x1E, |
| + 0xCC, 0x1E, 0xCC, 0x1E, 0xCE, 0x1E, 0xCE, 0x1E, 0xD0, 0x1E, 0xD0, 0x1E, 0xD2, 0x1E, 0xD2, 0x1E, |
| + 0xD4, 0x1E, 0xD4, 0x1E, 0xD6, 0x1E, 0xD6, 0x1E, 0xD8, 0x1E, 0xD8, 0x1E, 0xDA, 0x1E, 0xDA, 0x1E, |
| + 0xDC, 0x1E, 0xDC, 0x1E, 0xDE, 0x1E, 0xDE, 0x1E, 0xE0, 0x1E, 0xE0, 0x1E, 0xE2, 0x1E, 0xE2, 0x1E, |
| + 0xE4, 0x1E, 0xE4, 0x1E, 0xE6, 0x1E, 0xE6, 0x1E, 0xE8, 0x1E, 0xE8, 0x1E, 0xEA, 0x1E, 0xEA, 0x1E, |
| + 0xEC, 0x1E, 0xEC, 0x1E, 0xEE, 0x1E, 0xEE, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF2, 0x1E, 0xF2, 0x1E, |
| + 0xF4, 0x1E, 0xF4, 0x1E, 0xF6, 0x1E, 0xF6, 0x1E, 0xF8, 0x1E, 0xF8, 0x1E, 0xFA, 0x1E, 0xFB, 0x1E, |
| + 0xFC, 0x1E, 0xFD, 0x1E, 0xFE, 0x1E, 0xFF, 0x1E, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, |
| + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, |
| + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, |
| + 0x1C, 0x1F, 0x1D, 0x1F, 0x16, 0x1F, 0x17, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, |
| + 0x1C, 0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, |
| + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, |
| + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, |
| + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, |
| + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, |
| + 0x4C, 0x1F, 0x4D, 0x1F, 0x46, 0x1F, 0x47, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, |
| + 0x4C, 0x1F, 0x4D, 0x1F, 0x4E, 0x1F, 0x4F, 0x1F, 0x50, 0x1F, 0x59, 0x1F, 0x52, 0x1F, 0x5B, 0x1F, |
| + 0x54, 0x1F, 0x5D, 0x1F, 0x56, 0x1F, 0x5F, 0x1F, 0x58, 0x1F, 0x59, 0x1F, 0x5A, 0x1F, 0x5B, 0x1F, |
| + 0x5C, 0x1F, 0x5D, 0x1F, 0x5E, 0x1F, 0x5F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, |
| + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, |
| + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, |
| + 0xCA, 0x1F, 0xCB, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, |
| + 0xFA, 0x1F, 0xFB, 0x1F, 0x7E, 0x1F, 0x7F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, |
| + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, |
| + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, |
| + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, |
| + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, |
| + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, |
| + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xB2, 0x1F, 0xBC, 0x1F, |
| + 0xB4, 0x1F, 0xB5, 0x1F, 0xB6, 0x1F, 0xB7, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, |
| + 0xBC, 0x1F, 0xBD, 0x1F, 0xBE, 0x1F, 0xBF, 0x1F, 0xC0, 0x1F, 0xC1, 0x1F, 0xC2, 0x1F, 0xC3, 0x1F, |
| + 0xC4, 0x1F, 0xC5, 0x1F, 0xC6, 0x1F, 0xC7, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, 0xCA, 0x1F, 0xCB, 0x1F, |
| + 0xC3, 0x1F, 0xCD, 0x1F, 0xCE, 0x1F, 0xCF, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xD2, 0x1F, 0xD3, 0x1F, |
| + 0xD4, 0x1F, 0xD5, 0x1F, 0xD6, 0x1F, 0xD7, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, |
| + 0xDC, 0x1F, 0xDD, 0x1F, 0xDE, 0x1F, 0xDF, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xE2, 0x1F, 0xE3, 0x1F, |
| + 0xE4, 0x1F, 0xEC, 0x1F, 0xE6, 0x1F, 0xE7, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, |
| + 0xEC, 0x1F, 0xED, 0x1F, 0xEE, 0x1F, 0xEF, 0x1F, 0xF0, 0x1F, 0xF1, 0x1F, 0xF2, 0x1F, 0xF3, 0x1F, |
| + 0xF4, 0x1F, 0xF5, 0x1F, 0xF6, 0x1F, 0xF7, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xFA, 0x1F, 0xFB, 0x1F, |
| + 0xF3, 0x1F, 0xFD, 0x1F, 0xFE, 0x1F, 0xFF, 0x1F, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20, |
| + 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x09, 0x20, 0x0A, 0x20, 0x0B, 0x20, |
| + 0x0C, 0x20, 0x0D, 0x20, 0x0E, 0x20, 0x0F, 0x20, 0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20, |
| + 0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20, 0x18, 0x20, 0x19, 0x20, 0x1A, 0x20, 0x1B, 0x20, |
| + 0x1C, 0x20, 0x1D, 0x20, 0x1E, 0x20, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20, |
| + 0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20, 0x28, 0x20, 0x29, 0x20, 0x2A, 0x20, 0x2B, 0x20, |
| + 0x2C, 0x20, 0x2D, 0x20, 0x2E, 0x20, 0x2F, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20, |
| + 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20, 0x38, 0x20, 0x39, 0x20, 0x3A, 0x20, 0x3B, 0x20, |
| + 0x3C, 0x20, 0x3D, 0x20, 0x3E, 0x20, 0x3F, 0x20, 0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20, |
| + 0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20, 0x48, 0x20, 0x49, 0x20, 0x4A, 0x20, 0x4B, 0x20, |
| + 0x4C, 0x20, 0x4D, 0x20, 0x4E, 0x20, 0x4F, 0x20, 0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20, |
| + 0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20, 0x58, 0x20, 0x59, 0x20, 0x5A, 0x20, 0x5B, 0x20, |
| + 0x5C, 0x20, 0x5D, 0x20, 0x5E, 0x20, 0x5F, 0x20, 0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20, |
| + 0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20, 0x68, 0x20, 0x69, 0x20, 0x6A, 0x20, 0x6B, 0x20, |
| + 0x6C, 0x20, 0x6D, 0x20, 0x6E, 0x20, 0x6F, 0x20, 0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20, |
| + 0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20, 0x78, 0x20, 0x79, 0x20, 0x7A, 0x20, 0x7B, 0x20, |
| + 0x7C, 0x20, 0x7D, 0x20, 0x7E, 0x20, 0x7F, 0x20, 0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20, |
| + 0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x8A, 0x20, 0x8B, 0x20, |
| + 0x8C, 0x20, 0x8D, 0x20, 0x8E, 0x20, 0x8F, 0x20, 0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20, |
| + 0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20, 0x98, 0x20, 0x99, 0x20, 0x9A, 0x20, 0x9B, 0x20, |
| + 0x9C, 0x20, 0x9D, 0x20, 0x9E, 0x20, 0x9F, 0x20, 0xA0, 0x20, 0xA1, 0x20, 0xA2, 0x20, 0xA3, 0x20, |
| + 0xA4, 0x20, 0xA5, 0x20, 0xA6, 0x20, 0xA7, 0x20, 0xA8, 0x20, 0xA9, 0x20, 0xAA, 0x20, 0xAB, 0x20, |
| + 0xAC, 0x20, 0xAD, 0x20, 0xAE, 0x20, 0xAF, 0x20, 0xB0, 0x20, 0xB1, 0x20, 0xB2, 0x20, 0xB3, 0x20, |
| + 0xB4, 0x20, 0xB5, 0x20, 0xB6, 0x20, 0xB7, 0x20, 0xB8, 0x20, 0xB9, 0x20, 0xBA, 0x20, 0xBB, 0x20, |
| + 0xBC, 0x20, 0xBD, 0x20, 0xBE, 0x20, 0xBF, 0x20, 0xC0, 0x20, 0xC1, 0x20, 0xC2, 0x20, 0xC3, 0x20, |
| + 0xC4, 0x20, 0xC5, 0x20, 0xC6, 0x20, 0xC7, 0x20, 0xC8, 0x20, 0xC9, 0x20, 0xCA, 0x20, 0xCB, 0x20, |
| + 0xCC, 0x20, 0xCD, 0x20, 0xCE, 0x20, 0xCF, 0x20, 0xD0, 0x20, 0xD1, 0x20, 0xD2, 0x20, 0xD3, 0x20, |
| + 0xD4, 0x20, 0xD5, 0x20, 0xD6, 0x20, 0xD7, 0x20, 0xD8, 0x20, 0xD9, 0x20, 0xDA, 0x20, 0xDB, 0x20, |
| + 0xDC, 0x20, 0xDD, 0x20, 0xDE, 0x20, 0xDF, 0x20, 0xE0, 0x20, 0xE1, 0x20, 0xE2, 0x20, 0xE3, 0x20, |
| + 0xE4, 0x20, 0xE5, 0x20, 0xE6, 0x20, 0xE7, 0x20, 0xE8, 0x20, 0xE9, 0x20, 0xEA, 0x20, 0xEB, 0x20, |
| + 0xEC, 0x20, 0xED, 0x20, 0xEE, 0x20, 0xEF, 0x20, 0xF0, 0x20, 0xF1, 0x20, 0xF2, 0x20, 0xF3, 0x20, |
| + 0xF4, 0x20, 0xF5, 0x20, 0xF6, 0x20, 0xF7, 0x20, 0xF8, 0x20, 0xF9, 0x20, 0xFA, 0x20, 0xFB, 0x20, |
| + 0xFC, 0x20, 0xFD, 0x20, 0xFE, 0x20, 0xFF, 0x20, 0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21, |
| + 0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21, 0x08, 0x21, 0x09, 0x21, 0x0A, 0x21, 0x0B, 0x21, |
| + 0x0C, 0x21, 0x0D, 0x21, 0x0E, 0x21, 0x0F, 0x21, 0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21, |
| + 0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21, 0x18, 0x21, 0x19, 0x21, 0x1A, 0x21, 0x1B, 0x21, |
| + 0x1C, 0x21, 0x1D, 0x21, 0x1E, 0x21, 0x1F, 0x21, 0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21, |
| + 0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21, 0x28, 0x21, 0x29, 0x21, 0x2A, 0x21, 0x2B, 0x21, |
| + 0x2C, 0x21, 0x2D, 0x21, 0x2E, 0x21, 0x2F, 0x21, 0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21, |
| + 0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21, 0x38, 0x21, 0x39, 0x21, 0x3A, 0x21, 0x3B, 0x21, |
| + 0x3C, 0x21, 0x3D, 0x21, 0x3E, 0x21, 0x3F, 0x21, 0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21, |
| + 0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21, 0x48, 0x21, 0x49, 0x21, 0x4A, 0x21, 0x4B, 0x21, |
| + 0x4C, 0x21, 0x4D, 0x21, 0x32, 0x21, 0x4F, 0x21, 0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21, |
| + 0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21, 0x58, 0x21, 0x59, 0x21, 0x5A, 0x21, 0x5B, 0x21, |
| + 0x5C, 0x21, 0x5D, 0x21, 0x5E, 0x21, 0x5F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, |
| + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, |
| + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, |
| + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, |
| + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21, |
| + 0x83, 0x21, 0xFF, 0xFF, 0x4B, 0x03, 0xB6, 0x24, 0xB7, 0x24, 0xB8, 0x24, 0xB9, 0x24, 0xBA, 0x24, |
| + 0xBB, 0x24, 0xBC, 0x24, 0xBD, 0x24, 0xBE, 0x24, 0xBF, 0x24, 0xC0, 0x24, 0xC1, 0x24, 0xC2, 0x24, |
| + 0xC3, 0x24, 0xC4, 0x24, 0xC5, 0x24, 0xC6, 0x24, 0xC7, 0x24, 0xC8, 0x24, 0xC9, 0x24, 0xCA, 0x24, |
| + 0xCB, 0x24, 0xCC, 0x24, 0xCD, 0x24, 0xCE, 0x24, 0xCF, 0x24, 0xFF, 0xFF, 0x46, 0x07, 0x00, 0x2C, |
| + 0x01, 0x2C, 0x02, 0x2C, 0x03, 0x2C, 0x04, 0x2C, 0x05, 0x2C, 0x06, 0x2C, 0x07, 0x2C, 0x08, 0x2C, |
| + 0x09, 0x2C, 0x0A, 0x2C, 0x0B, 0x2C, 0x0C, 0x2C, 0x0D, 0x2C, 0x0E, 0x2C, 0x0F, 0x2C, 0x10, 0x2C, |
| + 0x11, 0x2C, 0x12, 0x2C, 0x13, 0x2C, 0x14, 0x2C, 0x15, 0x2C, 0x16, 0x2C, 0x17, 0x2C, 0x18, 0x2C, |
| + 0x19, 0x2C, 0x1A, 0x2C, 0x1B, 0x2C, 0x1C, 0x2C, 0x1D, 0x2C, 0x1E, 0x2C, 0x1F, 0x2C, 0x20, 0x2C, |
| + 0x21, 0x2C, 0x22, 0x2C, 0x23, 0x2C, 0x24, 0x2C, 0x25, 0x2C, 0x26, 0x2C, 0x27, 0x2C, 0x28, 0x2C, |
| + 0x29, 0x2C, 0x2A, 0x2C, 0x2B, 0x2C, 0x2C, 0x2C, 0x2D, 0x2C, 0x2E, 0x2C, 0x5F, 0x2C, 0x60, 0x2C, |
| + 0x60, 0x2C, 0x62, 0x2C, 0x63, 0x2C, 0x64, 0x2C, 0x65, 0x2C, 0x66, 0x2C, 0x67, 0x2C, 0x67, 0x2C, |
| + 0x69, 0x2C, 0x69, 0x2C, 0x6B, 0x2C, 0x6B, 0x2C, 0x6D, 0x2C, 0x6E, 0x2C, 0x6F, 0x2C, 0x70, 0x2C, |
| + 0x71, 0x2C, 0x72, 0x2C, 0x73, 0x2C, 0x74, 0x2C, 0x75, 0x2C, 0x75, 0x2C, 0x77, 0x2C, 0x78, 0x2C, |
| + 0x79, 0x2C, 0x7A, 0x2C, 0x7B, 0x2C, 0x7C, 0x2C, 0x7D, 0x2C, 0x7E, 0x2C, 0x7F, 0x2C, 0x80, 0x2C, |
| + 0x80, 0x2C, 0x82, 0x2C, 0x82, 0x2C, 0x84, 0x2C, 0x84, 0x2C, 0x86, 0x2C, 0x86, 0x2C, 0x88, 0x2C, |
| + 0x88, 0x2C, 0x8A, 0x2C, 0x8A, 0x2C, 0x8C, 0x2C, 0x8C, 0x2C, 0x8E, 0x2C, 0x8E, 0x2C, 0x90, 0x2C, |
| + 0x90, 0x2C, 0x92, 0x2C, 0x92, 0x2C, 0x94, 0x2C, 0x94, 0x2C, 0x96, 0x2C, 0x96, 0x2C, 0x98, 0x2C, |
| + 0x98, 0x2C, 0x9A, 0x2C, 0x9A, 0x2C, 0x9C, 0x2C, 0x9C, 0x2C, 0x9E, 0x2C, 0x9E, 0x2C, 0xA0, 0x2C, |
| + 0xA0, 0x2C, 0xA2, 0x2C, 0xA2, 0x2C, 0xA4, 0x2C, 0xA4, 0x2C, 0xA6, 0x2C, 0xA6, 0x2C, 0xA8, 0x2C, |
| + 0xA8, 0x2C, 0xAA, 0x2C, 0xAA, 0x2C, 0xAC, 0x2C, 0xAC, 0x2C, 0xAE, 0x2C, 0xAE, 0x2C, 0xB0, 0x2C, |
| + 0xB0, 0x2C, 0xB2, 0x2C, 0xB2, 0x2C, 0xB4, 0x2C, 0xB4, 0x2C, 0xB6, 0x2C, 0xB6, 0x2C, 0xB8, 0x2C, |
| + 0xB8, 0x2C, 0xBA, 0x2C, 0xBA, 0x2C, 0xBC, 0x2C, 0xBC, 0x2C, 0xBE, 0x2C, 0xBE, 0x2C, 0xC0, 0x2C, |
| + 0xC0, 0x2C, 0xC2, 0x2C, 0xC2, 0x2C, 0xC4, 0x2C, 0xC4, 0x2C, 0xC6, 0x2C, 0xC6, 0x2C, 0xC8, 0x2C, |
| + 0xC8, 0x2C, 0xCA, 0x2C, 0xCA, 0x2C, 0xCC, 0x2C, 0xCC, 0x2C, 0xCE, 0x2C, 0xCE, 0x2C, 0xD0, 0x2C, |
| + 0xD0, 0x2C, 0xD2, 0x2C, 0xD2, 0x2C, 0xD4, 0x2C, 0xD4, 0x2C, 0xD6, 0x2C, 0xD6, 0x2C, 0xD8, 0x2C, |
| + 0xD8, 0x2C, 0xDA, 0x2C, 0xDA, 0x2C, 0xDC, 0x2C, 0xDC, 0x2C, 0xDE, 0x2C, 0xDE, 0x2C, 0xE0, 0x2C, |
| + 0xE0, 0x2C, 0xE2, 0x2C, 0xE2, 0x2C, 0xE4, 0x2C, 0xE5, 0x2C, 0xE6, 0x2C, 0xE7, 0x2C, 0xE8, 0x2C, |
| + 0xE9, 0x2C, 0xEA, 0x2C, 0xEB, 0x2C, 0xEC, 0x2C, 0xED, 0x2C, 0xEE, 0x2C, 0xEF, 0x2C, 0xF0, 0x2C, |
| + 0xF1, 0x2C, 0xF2, 0x2C, 0xF3, 0x2C, 0xF4, 0x2C, 0xF5, 0x2C, 0xF6, 0x2C, 0xF7, 0x2C, 0xF8, 0x2C, |
| + 0xF9, 0x2C, 0xFA, 0x2C, 0xFB, 0x2C, 0xFC, 0x2C, 0xFD, 0x2C, 0xFE, 0x2C, 0xFF, 0x2C, 0xA0, 0x10, |
| + 0xA1, 0x10, 0xA2, 0x10, 0xA3, 0x10, 0xA4, 0x10, 0xA5, 0x10, 0xA6, 0x10, 0xA7, 0x10, 0xA8, 0x10, |
| + 0xA9, 0x10, 0xAA, 0x10, 0xAB, 0x10, 0xAC, 0x10, 0xAD, 0x10, 0xAE, 0x10, 0xAF, 0x10, 0xB0, 0x10, |
| + 0xB1, 0x10, 0xB2, 0x10, 0xB3, 0x10, 0xB4, 0x10, 0xB5, 0x10, 0xB6, 0x10, 0xB7, 0x10, 0xB8, 0x10, |
| + 0xB9, 0x10, 0xBA, 0x10, 0xBB, 0x10, 0xBC, 0x10, 0xBD, 0x10, 0xBE, 0x10, 0xBF, 0x10, 0xC0, 0x10, |
| + 0xC1, 0x10, 0xC2, 0x10, 0xC3, 0x10, 0xC4, 0x10, 0xC5, 0x10, 0xFF, 0xFF, 0x1B, 0xD2, 0x21, 0xFF, |
| + 0x22, 0xFF, 0x23, 0xFF, 0x24, 0xFF, 0x25, 0xFF, 0x26, 0xFF, 0x27, 0xFF, 0x28, 0xFF, 0x29, 0xFF, |
| + 0x2A, 0xFF, 0x2B, 0xFF, 0x2C, 0xFF, 0x2D, 0xFF, 0x2E, 0xFF, 0x2F, 0xFF, 0x30, 0xFF, 0x31, 0xFF, |
| + 0x32, 0xFF, 0x33, 0xFF, 0x34, 0xFF, 0x35, 0xFF, 0x36, 0xFF, 0x37, 0xFF, 0x38, 0xFF, 0x39, 0xFF, |
| + 0x3A, 0xFF, 0x5B, 0xFF, 0x5C, 0xFF, 0x5D, 0xFF, 0x5E, 0xFF, 0x5F, 0xFF, 0x60, 0xFF, 0x61, 0xFF, |
| + 0x62, 0xFF, 0x63, 0xFF, 0x64, 0xFF, 0x65, 0xFF, 0x66, 0xFF, 0x67, 0xFF, 0x68, 0xFF, 0x69, 0xFF, |
| + 0x6A, 0xFF, 0x6B, 0xFF, 0x6C, 0xFF, 0x6D, 0xFF, 0x6E, 0xFF, 0x6F, 0xFF, 0x70, 0xFF, 0x71, 0xFF, |
| + 0x72, 0xFF, 0x73, 0xFF, 0x74, 0xFF, 0x75, 0xFF, 0x76, 0xFF, 0x77, 0xFF, 0x78, 0xFF, 0x79, 0xFF, |
| + 0x7A, 0xFF, 0x7B, 0xFF, 0x7C, 0xFF, 0x7D, 0xFF, 0x7E, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0x81, 0xFF, |
| + 0x82, 0xFF, 0x83, 0xFF, 0x84, 0xFF, 0x85, 0xFF, 0x86, 0xFF, 0x87, 0xFF, 0x88, 0xFF, 0x89, 0xFF, |
| + 0x8A, 0xFF, 0x8B, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF, 0x8E, 0xFF, 0x8F, 0xFF, 0x90, 0xFF, 0x91, 0xFF, |
| + 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, 0xFF, 0x96, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF, |
| + 0x9A, 0xFF, 0x9B, 0xFF, 0x9C, 0xFF, 0x9D, 0xFF, 0x9E, 0xFF, 0x9F, 0xFF, 0xA0, 0xFF, 0xA1, 0xFF, |
| + 0xA2, 0xFF, 0xA3, 0xFF, 0xA4, 0xFF, 0xA5, 0xFF, 0xA6, 0xFF, 0xA7, 0xFF, 0xA8, 0xFF, 0xA9, 0xFF, |
| + 0xAA, 0xFF, 0xAB, 0xFF, 0xAC, 0xFF, 0xAD, 0xFF, 0xAE, 0xFF, 0xAF, 0xFF, 0xB0, 0xFF, 0xB1, 0xFF, |
| + 0xB2, 0xFF, 0xB3, 0xFF, 0xB4, 0xFF, 0xB5, 0xFF, 0xB6, 0xFF, 0xB7, 0xFF, 0xB8, 0xFF, 0xB9, 0xFF, |
| + 0xBA, 0xFF, 0xBB, 0xFF, 0xBC, 0xFF, 0xBD, 0xFF, 0xBE, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, |
| + 0xC2, 0xFF, 0xC3, 0xFF, 0xC4, 0xFF, 0xC5, 0xFF, 0xC6, 0xFF, 0xC7, 0xFF, 0xC8, 0xFF, 0xC9, 0xFF, |
| + 0xCA, 0xFF, 0xCB, 0xFF, 0xCC, 0xFF, 0xCD, 0xFF, 0xCE, 0xFF, 0xCF, 0xFF, 0xD0, 0xFF, 0xD1, 0xFF, |
| + 0xD2, 0xFF, 0xD3, 0xFF, 0xD4, 0xFF, 0xD5, 0xFF, 0xD6, 0xFF, 0xD7, 0xFF, 0xD8, 0xFF, 0xD9, 0xFF, |
| + 0xDA, 0xFF, 0xDB, 0xFF, 0xDC, 0xFF, 0xDD, 0xFF, 0xDE, 0xFF, 0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF, |
| + 0xE2, 0xFF, 0xE3, 0xFF, 0xE4, 0xFF, 0xE5, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF, |
| + 0xEA, 0xFF, 0xEB, 0xFF, 0xEC, 0xFF, 0xED, 0xFF, 0xEE, 0xFF, 0xEF, 0xFF, 0xF0, 0xFF, 0xF1, 0xFF, |
| + 0xF2, 0xFF, 0xF3, 0xFF, 0xF4, 0xFF, 0xF5, 0xFF, 0xF6, 0xFF, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xFF, |
| + 0xFA, 0xFF, 0xFB, 0xFF, 0xFC, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF |
| +}; |
| diff --git a/drivers/staging/exfat/exfat_version.h b/drivers/staging/exfat/exfat_version.h |
| new file mode 100644 |
| index 00000000..ab6ce18b |
| --- /dev/null |
| +++ b/drivers/staging/exfat/exfat_version.h |
| @@ -0,0 +1 @@ |
| +#define EXFAT_VERSION "1.2.4" |
| -- |
| 1.8.4.3.gca3854a |
| |