[PATCH] add exFAT driver

From: Benjamin Valentin
Date: Fri Aug 30 2013 - 06:53:18 EST


Samsung released their exFAT (and FAT12/16/32) driver under the GPLv2, it is
based on the in-kernel FAT driver and can be obtained from [1].
This patch adds version 1.2.4 of the exfat driver, sans the ifdefs for older
kernel versions and ported to Linux 3.11.

[1] http://opensource.samsung.com search for exfat

original authors: Joosun Hahn
Sung-Kwan Kim

Signed-off-by: Benjamin Valentin <benpicco@xxxxxxxxxxxxxxxxxx>
---
drivers/staging/Kconfig | 2 +
drivers/staging/Makefile | 1 +
drivers/staging/exfat/Kconfig | 25 +
drivers/staging/exfat/Makefile | 10 +
drivers/staging/exfat/TODO | 3 +
drivers/staging/exfat/exfat.c | 4941 +++++++++++++++++++++++++++++++++
drivers/staging/exfat/exfat.h | 613 ++++
drivers/staging/exfat/exfat_api.c | 458 +++
drivers/staging/exfat/exfat_api.h | 164 ++
drivers/staging/exfat/exfat_blkdev.c | 155 ++
drivers/staging/exfat/exfat_blkdev.h | 45 +
drivers/staging/exfat/exfat_cache.c | 741 +++++
drivers/staging/exfat/exfat_cache.h | 62 +
drivers/staging/exfat/exfat_data.c | 39 +
drivers/staging/exfat/exfat_data.h | 48 +
drivers/staging/exfat/exfat_global.c | 118 +
drivers/staging/exfat/exfat_global.h | 126 +
drivers/staging/exfat/exfat_nls.c | 353 +++
drivers/staging/exfat/exfat_nls.h | 69 +
drivers/staging/exfat/exfat_oal.c | 156 ++
drivers/staging/exfat/exfat_oal.h | 48 +
drivers/staging/exfat/exfat_part.h | 56 +
drivers/staging/exfat/exfat_super.c | 2009 ++++++++++++++
drivers/staging/exfat/exfat_super.h | 153 +
drivers/staging/exfat/exfat_upcase.c | 389 +++
drivers/staging/exfat/exfat_version.h | 1 +
26 files changed, 10785 insertions(+)
create mode 100644 drivers/staging/exfat/Kconfig
create mode 100644 drivers/staging/exfat/Makefile
create mode 100644 drivers/staging/exfat/TODO
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_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/Kconfig b/drivers/staging/Kconfig
index 57d8b34..d4c3b4b 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -142,4 +142,6 @@ source "drivers/staging/lustre/Kconfig"

source "drivers/staging/btmtk_usb/Kconfig"

+source "drivers/staging/exfat/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 429321f..370e314 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -63,3 +63,4 @@ obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_USB_DWC2) += dwc2/
obj-$(CONFIG_LUSTRE_FS) += lustre/
obj-$(CONFIG_USB_BTMTK) += btmtk_usb/
+obj-$(CONFIG_EXFAT_FS) += exfat/
diff --git a/drivers/staging/exfat/Kconfig b/drivers/staging/exfat/Kconfig
new file mode 100644
index 0000000..1f91227
--- /dev/null
+++ b/drivers/staging/exfat/Kconfig
@@ -0,0 +1,25 @@
+config EXFAT_FS
+ tristate "exFAT fs support"
+ select NLS
+ help
+ This adds the exFAT file system
+
+if EXFAT_FS
+
+config EXFAT_LITTLE_ENDIAN
+ bool "Little Endian Mode"
+ default y
+
+config EXFAT_DISCARD
+ bool "enable discard"
+ default y
+
+config EXFAT_KERNEL_DEBUG
+ bool "enable debugging"
+ default y
+
+config EXFAT_DEBUG_MSG
+ bool "print debug messages"
+ default n
+
+endif # EXFAT_FS
diff --git a/drivers/staging/exfat/Makefile b/drivers/staging/exfat/Makefile
new file mode 100644
index 0000000..d7fabfa
--- /dev/null
+++ b/drivers/staging/exfat/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the Linux fat filesystem support.
+#
+
+obj-$(CONFIG_EXFAT_FS) += 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
diff --git a/drivers/staging/exfat/TODO b/drivers/staging/exfat/TODO
new file mode 100644
index 0000000..707310a
--- /dev/null
+++ b/drivers/staging/exfat/TODO
@@ -0,0 +1,3 @@
+* fix coding style
+* remove dead code
+* merge with existing FAT implementation
diff --git a/drivers/staging/exfat/exfat.c b/drivers/staging/exfat/exfat.c
new file mode 100644
index 0000000..c8f9719
--- /dev/null
+++ b/drivers/staging/exfat/exfat.c
@@ -0,0 +1,4941 @@
+/* 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@xxxxxxxxxxxx)
+ */
+
+/*
+ * 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_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)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ sbi->s_dirt = 1;
+}
+
+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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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;
+#ifdef CONFIG_EXFAT_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));
+
+#ifdef CONFIG_EXFAT_DISCARD
+ if (opts->discard) {
+ ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits), GFP_NOFS, 0);
+ if (ret == -EOPNOTSUPP) {
+ 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, &sector);
+ 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, &sector);
+ if (!file_ep)
+ return FFS_MEDIAERR;
+
+ strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector);
+ 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, &sector_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, &sector_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, &sector_old);
+ buf_lock(sb, sector_old);
+ epnew = get_entry_in_dir(sb, p_dir, newentry+1, &sector_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, &sector_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, &sector_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, &sector_mov);
+ buf_lock(sb, sector_mov);
+ epnew = get_entry_in_dir(sb, p_newdir, newentry+1, &sector_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, &sector_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 0000000..0d3c841
--- /dev/null
+++ b/drivers/staging/exfat/exfat.h
@@ -0,0 +1,613 @@
+/*
+ * 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_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
+
+#ifdef CONFIG_EXFAT_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 // max num of volumes per device
+
+#define DENTRY_SIZE 32 // dir entry size
+#define DENTRY_SIZE_BITS 5
+
+#define PBR_SIGNATURE 0xAA55
+#define EXT_SIGNATURE 0xAA550000
+#define VOL_LABEL "NO NAME " // size should be 11
+#define OEM_NAME "MSWIN4.1" // size should be 8
+#define STR_FAT12 "FAT12 " // size should be 8
+#define STR_FAT16 "FAT16 " // size should be 8
+#define STR_FAT32 "FAT32 " // size should be 8
+#define STR_EXFAT "EXFAT " // size should be 8
+#define VOL_CLEAN 0x0000
+#define VOL_DIRTY 0x0002
+
+/* max number of clusters */
+#define FAT12_THRESHOLD 4087 // 2^12 - 1 + 2 (clu 0 & 1)
+#define FAT16_THRESHOLD 65527 // 2^16 - 1 + 2
+#define FAT32_THRESHOLD 268435457 // 2^28 - 1 + 2
+#define EXFAT_THRESHOLD 268435457 // 2^28 - 1 + 2
+
+#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)
+
+#ifdef CONFIG_EXFAT_LITTLE_ENDIAN
+#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;
+ }
+
+ /* MS_DOS FAT partition boot record (512 bytes) */
+ typedef struct {
+ UINT8 jmp_boot[3];
+ UINT8 oem_name[8];
+ UINT8 bpb[109];
+ UINT8 boot_code[390];
+ UINT8 signature[2];
+ } PBR_SECTOR_T;
+
+ /* MS-DOS FAT12/16 BIOS parameter block (51 bytes) */
+ 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;
+
+ /* MS-DOS FAT32 BIOS parameter block (79 bytes) */
+ 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;
+
+ /* MS-DOS EXFAT BIOS parameter block (109 bytes) */
+ 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;
+
+ /* MS-DOS FAT file system information sector (512 bytes) */
+ 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;
+
+ /* MS-DOS FAT directory entry (32 bytes) */
+ 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;
+
+ /* MS-DOS FAT extended directory entry (32 bytes) */
+ 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;
+
+ /* MS-DOS EXFAT file directory entry (32 bytes) */
+ 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;
+
+ /* MS-DOS EXFAT stream extension directory entry (32 bytes) */
+ 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;
+
+ /* MS-DOS EXFAT file name directory entry (32 bytes) */
+ typedef struct {
+ UINT8 type;
+ UINT8 flags;
+ UINT8 unicode_0_14[30];
+ } NAME_DENTRY_T;
+
+ /* MS-DOS EXFAT allocation bitmap directory entry (32 bytes) */
+ typedef struct {
+ UINT8 type;
+ UINT8 flags;
+ UINT8 reserved[18];
+ UINT8 start_clu[4];
+ UINT8 size[8];
+ } BMAP_DENTRY_T;
+
+ /* MS-DOS EXFAT up-case table directory entry (32 bytes) */
+ typedef struct {
+ UINT8 type;
+ UINT8 reserved1[3];
+ UINT8 checksum[4];
+ UINT8 reserved2[12];
+ UINT8 start_clu[4];
+ UINT8 size[8];
+ } CASE_DENTRY_T;
+
+ /* MS-DOS EXFAT volume label directory entry (32 bytes) */
+ typedef struct {
+ UINT8 type;
+ UINT8 label_len;
+ UINT8 unicode_0_10[22];
+ UINT8 reserved[8];
+ } VOLM_DENTRY_T;
+
+ /* unused entry hint information */
+ typedef struct {
+ UINT32 dir;
+ INT32 entry;
+ CHAIN_T clu;
+ } UENTRY_T;
+
+ /* file system volume information structure */
+ 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; // drive ID
+ UINT32 vol_type; // volume FAT type
+ UINT32 vol_id; // volume serial number
+
+ UINT32 num_sectors; // num of sectors in volume
+ UINT32 num_clusters; // num of clusters in volume
+ UINT32 cluster_size; // cluster size in bytes
+ UINT32 cluster_size_bits;
+ UINT32 sectors_per_clu; // cluster size in sectors
+ UINT32 sectors_per_clu_bits;
+
+ UINT32 PBR_sector; // PBR sector
+ UINT32 FAT1_start_sector; // FAT1 start sector
+ UINT32 FAT2_start_sector; // FAT2 start sector
+ UINT32 root_start_sector; // root dir start sector
+ UINT32 data_start_sector; // data area start sector
+ UINT32 num_FAT_sectors; // num of FAT sectors
+
+ UINT32 root_dir; // root dir cluster
+ UINT32 dentries_in_root; // num of dentries in root dir
+ UINT32 dentries_per_clu; // num of dentries per cluster
+
+ UINT32 vol_flag; // volume dirty flag
+ struct buffer_head *pbr_bh; // PBR sector
+
+ UINT32 map_clu; // allocation bitmap start cluster
+ UINT32 map_sectors; // num of allocation bitmap sectors
+ struct buffer_head **vol_amap; // allocation bitmap
+
+ UINT16 **vol_utbl; // upcase table
+
+ UINT32 clu_srch_ptr; // cluster search pointer
+ UINT32 used_clusters; // number of used clusters
+ UENTRY_T hint_uentry; // unused entry hint information
+
+ UINT32 dev_ejected; // block device operation error flag
+
+ 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; // sector number that contains file_entry
+ INT32 offset; // byte offset in the sector
+ INT32 alloc_flag; // flag in stream entry. 01 for cluster chain, 03 for contig. clusteres.
+ 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 0000000..ea797b2
--- /dev/null
+++ b/drivers/staging/exfat/exfat_api.c
@@ -0,0 +1,458 @@
+/*
+ * 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_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;
+
+ /* initialize all volumes as un-mounted */
+ 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;
+
+ /* unmount all volumes */
+ 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);
+
+#ifdef CONFIG_EXFAT_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 0000000..ac2c768
--- /dev/null
+++ b/drivers/staging/exfat/exfat_api.h
@@ -0,0 +1,164 @@
+/*
+ * 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_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 /* max size of multi-byte character */
+#define MAX_PATH_DEPTH 15 /* max depth of path name */
+#define MAX_NAME_LENGTH 256 /* max len of file name including NULL */
+#define MAX_PATH_LENGTH 260 /* max len of path name including NULL */
+#define DOS_NAME_LENGTH 11 /* DOS file name length excluding NULL */
+#define DOS_PATH_LENGTH 80 /* DOS path name length excluding NULL */
+
+#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; // start sector number of the partition
+ UINT32 Size; // in sectors
+ } PART_INFO_T;
+
+ typedef struct {
+ UINT32 SecSize; // sector size in bytes
+ UINT32 DevSize; // block device size in sectors
+ } 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]; // used only for FAT12/16/32, not used for exFAT
+ 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 0000000..0f6358c
--- /dev/null
+++ b/drivers/staging/exfat/exfat_blkdev.c
@@ -0,0 +1,155 @@
+/*
+ * 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_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);
+#ifdef CONFIG_EXFAT_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);
+#ifdef CONFIG_EXFAT_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);
+#ifdef CONFIG_EXFAT_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 0000000..50941b9
--- /dev/null
+++ b/drivers/staging/exfat/exfat_blkdev.h
@@ -0,0 +1,45 @@
+/*
+ * 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_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 0000000..79988ee
--- /dev/null
+++ b/drivers/staging/exfat/exfat_cache.c
@@ -0,0 +1,741 @@
+/*
+ * 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_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;
+
+ /* LRU list */
+ 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);
+ }
+
+ /* HASH 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 0000000..4905b23
--- /dev/null
+++ b/drivers/staging/exfat/exfat_cache.h
@@ -0,0 +1,62 @@
+/*
+ * 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_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_data.c b/drivers/staging/exfat/exfat_data.c
new file mode 100644
index 0000000..8c5dd88
--- /dev/null
+++ b/drivers/staging/exfat/exfat_data.c
@@ -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.
+ */
+
+#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];
+
+DEFINE_SEMAPHORE(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];
+
+DEFINE_SEMAPHORE(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 0000000..fc06244
--- /dev/null
+++ b/drivers/staging/exfat/exfat_data.h
@@ -0,0 +1,48 @@
+/*
+ * 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_global.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define MAX_DEVICE 2
+
+/* max number of volumes on all block devices */
+#define MAX_DRIVE 2
+
+/* max number of open files */
+#define MAX_OPEN 20
+
+/* max number of root directory entries in FAT12/16 */
+/* (should be an exponential value of 2) */
+#define MAX_DENTRY 512
+
+/* cache size (in number of sectors) */
+/* (should be an exponential value of 2) */
+#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 0000000..d6cf38a
--- /dev/null
+++ b/drivers/staging/exfat/exfat_global.c
@@ -0,0 +1,118 @@
+/*
+ * 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_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 0000000..f9222a6
--- /dev/null
+++ b/drivers/staging/exfat/exfat_global.h
@@ -0,0 +1,126 @@
+/*
+ * 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>
+
+
+#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)
+
+#ifdef CONFIG_EXFAT_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 0000000..841777f
--- /dev/null
+++ b/drivers/staging/exfat/exfat_nls.c
@@ -0,0 +1,353 @@
+/*
+ * 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_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;
+ }
+
+ /* search for the last embedded period */
+ 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;
+
+
+ /* strip all trailing spaces */
+ 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, "..")) {
+
+ /* strip all trailing periods */
+ 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 0000000..761fd71
--- /dev/null
+++ b/drivers/staging/exfat/exfat_nls.h
@@ -0,0 +1,69 @@
+/*
+ * 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_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 ".. "
+
+#ifdef CONFIG_EXFAT_LITTLE_ENDIAN
+#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
+
+
+/* DOS name stucture */
+typedef struct {
+ UINT8 name[DOS_NAME_LENGTH];
+ UINT8 name_case;
+} DOS_NAME_T;
+
+/* unicode name stucture */
+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 0000000..cf73250
--- /dev/null
+++ b/drivers/staging/exfat/exfat_oal.c
@@ -0,0 +1,156 @@
+/* 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@xxxxxxxxxxxx)
+ */
+
+/*
+ * 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_global.h"
+#include "exfat_api.h"
+#include "exfat_oal.h"
+
+DEFINE_SEMAPHORE(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;
+
+/*
+ * The epoch of FAT timestamp is 1980.
+ * : bits : value
+ * date: 0 - 4: day (1 - 31)
+ * date: 5 - 8: month (1 - 12)
+ * date: 9 - 15: year (0 - 127) from 1980
+ * time: 0 - 4: sec (0 - 29) 2sec counts
+ * time: 5 - 10: min (0 - 59)
+ * time: 11 - 15: hour (0 - 23)
+ */
+#define UNIX_SECS_1980 315532800L
+
+#if BITS_PER_LONG == 64
+#define UNIX_SECS_2108 4354819200L
+#endif
+/* days between 1.1.70 and 1.1.80 (2 leap days) */
+#define DAYS_DELTA_DECADE (365 * 10 + 2)
+/* 120 (2100 - 1980) isn't leap year */
+#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)
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+static time_t accum_days_in_year[] = {
+ /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
+ 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;
+
+ /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
+ 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 0000000..ae67a95
--- /dev/null
+++ b/drivers/staging/exfat/exfat_oal.h
@@ -0,0 +1,48 @@
+/*
+ * 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_global.h"
+#include <linux/version.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ typedef struct {
+ UINT16 sec; /* 0 ~ 59 */
+ UINT16 min; /* 0 ~ 59 */
+ UINT16 hour; /* 0 ~ 23 */
+ UINT16 day; /* 1 ~ 31 */
+ UINT16 mon; /* 1 ~ 12 */
+ UINT16 year; /* 0 ~ 127 (since 1980) */
+ } TIMESTAMP_T;
+
+ 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 0000000..9ef2195
--- /dev/null
+++ b/drivers/staging/exfat/exfat_part.h
@@ -0,0 +1,56 @@
+/*
+ * 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_global.h"
+#include "exfat_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MBR_SIGNATURE 0xAA55
+
+ /* MS-DOS FAT master boot record (512 bytes) */
+ typedef struct {
+ UINT8 boot_code[446];
+ UINT8 partition[64];
+ UINT8 signature[2];
+ } MBR_SECTOR_T;
+
+ /* MS-DOS FAT partition table (64 bytes) */
+ 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 0000000..3fc60b9
--- /dev/null
+++ b/drivers/staging/exfat/exfat_super.c
@@ -0,0 +1,2009 @@
+/* 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@xxxxxxxxxxxx)
+ */
+
+/*
+ * 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>
+#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/aio.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_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
+/* days between 1.1.70 and 1.1.80 (2 leap days) */
+#define DAYS_DELTA_DECADE (365 * 10 + 2)
+/* 120 (2100 - 1980) isn't leap year */
+#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)
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+static time_t accum_days_in_year[] = {
+ /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
+ 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);
+
+/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
+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;
+}
+
+/* Convert linear UNIX date to a FAT time/date pair. */
+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;
+
+ /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
+ 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);
+static long exfat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+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)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ mutex_lock(&sbi->s_lock);
+}
+
+static void __unlock_super(struct super_block *sb)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ mutex_unlock(&sbi->s_lock);
+}
+
+static int __is_sb_dirty(struct super_block *sb)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ return sbi->s_dirt;
+}
+
+static void __set_sb_clean(struct super_block *sb)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ sbi->s_dirt = 0;
+}
+
+static int exfat_readdir(struct file *filp, struct dir_context *ctx)
+{
+ struct inode *inode = file_inode(filp);
+ 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 = ctx->pos;
+ /* Fake . and .. for the root directory. */
+ 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 /* (cpos == 1) */
+ inum = parent_ino(filp->f_path.dentry);
+
+ if (!dir_emit_dots(filp, ctx))
+ goto out;
+ cpos++;
+ ctx->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) {
+ /* at least we tried to read a sector
+ * move cpos to next sector position (should be aligned)
+ */
+ 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 (!dir_emit(ctx, de.Name, strlen(de.Name), inum,
+ (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG))
+ goto out;
+
+ ctx->pos = cpos;
+ goto get_new;
+
+end_of_dir:
+ ctx->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;
+}
+
+static long exfat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+ struct inode *inode = filp->f_dentry->d_inode;
+ unsigned int flags;
+#endif
+
+ switch (cmd) {
+ case EXFAT_IOCTL_GET_VOLUME_ID:
+ return exfat_ioctl_volume_id(inode);
+#ifdef CONFIG_EXFAT_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;
+ }
+}
+
+const struct file_operations exfat_dir_operations = {
+ .llseek = generic_file_llseek,
+ .read = generic_read_dir,
+ .iterate = exfat_readdir,
+ .unlocked_ioctl = exfat_generic_ioctl,
+ .fsync = generic_file_fsync,
+};
+
+static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl)
+{
+ 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;
+ /* timestamp is already written, so mark_inode_dirty() is unnecessary. */
+
+ 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);
+}
+
+static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode;
+ struct dentry *alias;
+ 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;
+ dentry = d_splice_alias(inode, dentry);
+ if (dentry)
+ dentry->d_time = dentry->d_parent->d_inode->i_version;
+ 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;
+ /* timestamp is already written, so mark_inode_dirty() is unneeded. */
+
+ 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;
+}
+
+static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+ 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;
+ /* timestamp is already written, so mark_inode_dirty() is unneeded. */
+
+ 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;
+ }
+
+ /* use a default check */
+ 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);
+
+ /* Of the r and x bits, all (subject to umask) must be present.*/
+ if ((perm & (S_IRUGO | S_IXUGO)) != (i_mode & (S_IRUGO|S_IXUGO)))
+ return -EPERM;
+
+ if (exfat_mode_can_hold_ro(inode)) {
+ /* Of the w bits, either all (subject to umask) or none must be present. */
+ if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask)))
+ return -EPERM;
+ } else {
+ /* If exfat_mode_can_hold_ro(inode) is false, can't change w bits. */
+ 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;
+ }
+
+ /*
+ * We don't return -EPERM here. Yes, strange, but this is too
+ * old behavior.
+ */
+ if (attr->ia_valid & ATTR_MODE) {
+ if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0)
+ attr->ia_valid &= ~ATTR_MODE;
+ }
+
+ EXFAT_I(inode)->fid.size = i_size_read(inode);
+
+ if (attr->ia_valid & ATTR_SIZE) {
+ old_size = i_size_read(inode);
+ 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);
+ }
+ setattr_copy(inode, attr);
+ mark_inode_dirty(inode);
+
+ 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,
+ .unlocked_ioctl = exfat_generic_ioctl,
+ .fsync = generic_file_fsync,
+ .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);
+
+ /*
+ * This protects against truncating a file bigger than it was then
+ * trying to write into the hole.
+ */
+ 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);
+}
+
+const struct inode_operations exfat_file_inode_operations = {
+ .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;
+}
+
+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));
+ }
+}
+
+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 (ret < 0)
+ exfat_write_failed(mapping, pos+len);
+ return ret;
+}
+
+static int exfat_write_end(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned 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 (err < len)
+ exfat_write_failed(mapping, pos+len);
+
+ 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;
+ struct address_space *mapping = iocb->ki_filp->f_mapping;
+ ssize_t ret;
+
+ if (rw == WRITE) {
+ if (EXFAT_I(inode)->mmu_private < (offset + iov_length(iov, nr_segs)))
+ return 0;
+ }
+ ret = blockdev_direct_IO(rw, iocb, inode, iov,
+ offset, nr_segs, exfat_get_block);
+
+ if ((ret < 0) && (rw & WRITE))
+ exfat_write_failed(mapping, offset+iov_length(iov, nr_segs));
+
+ return ret;
+}
+
+static sector_t _exfat_bmap(struct address_space *mapping, sector_t block)
+{
+ sector_t blocknr;
+
+ /* exfat_get_cluster() assumes the requested blocknr isn't truncated. */
+ down_read(&EXFAT_I(mapping->host)->truncate_lock);
+ blocknr = generic_block_bmap(mapping, block, exfat_get_block);
+ up_read(&EXFAT_I(mapping->host)->truncate_lock);
+
+ return blocknr;
+}
+
+const struct address_space_operations exfat_aops = {
+ .readpage = exfat_readpage,
+ .readpages = exfat_readpages,
+ .writepage = exfat_writepage,
+ .writepages = exfat_writepages,
+ .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_head *head = sbi->inode_hashtable + exfat_hash(i_pos);
+ struct inode *inode = NULL;
+
+ spin_lock(&sbi->inode_hash_lock);
+ hlist_for_each_entry(info, head, i_hash_fat) {
+ 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);
+}
+
+/* doesn't deal with root inode */
+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) { /* directory */
+ 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);
+ set_nlink(inode,info.NumSubdirs);
+ } else if (info.Attr & ATTR_SYMLINK) { /* symbolic link */
+ 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 { /* regular file */
+ 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;
+
+ init_rwsem(&ei->truncate_lock);
+
+ 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;
+}
+
+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);
+ clear_inode(inode);
+ exfat_detach(inode);
+
+ remove_inode_hash(inode);
+}
+
+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;
+}
+
+static int exfat_show_options(struct seq_file *m, struct dentry *root)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(root->d_sb);
+ 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");
+#ifdef CONFIG_EXFAT_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,
+ .evict_inode = exfat_evict_inode,
+ .put_super = exfat_put_super,
+ .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,
+#ifdef CONFIG_EXFAT_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"},
+#ifdef CONFIG_EXFAT_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;
+#ifdef CONFIG_EXFAT_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;
+#ifdef CONFIG_EXFAT_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;
+ set_nlink(inode,info.NumSubdirs + 2);
+
+ 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];
+
+ /*
+ * GFP_KERNEL is ok here, because while we do hold the
+ * supeblock lock, memory pressure can't call back into
+ * the filesystem, since we're only just about to mount
+ * it and have no inodes etc active!
+ */
+ sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL);
+ if (!sbi)
+ return -ENOMEM;
+
+ mutex_init(&sbi->s_lock);
+
+ 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;
+ }
+
+ /* set up enough so that it can read an inode */
+ exfat_hash_init(sb);
+
+ /*
+ * The low byte of FAT's first entry must have same value with
+ * media-field. But in real world, too many devices is
+ * writing wrong value. So, removed that validity check.
+ *
+ * if (FAT_FIRST_ENT(sb, media) != first)
+ */
+
+ 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);
+ sb->s_root = d_make_root(root_inode);
+
+ 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;
+}
+
+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);
+}
+
+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);
+}
+
+#ifdef CONFIG_EXFAT_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) {
+ /* invalidate_bdev drops all device cache include dirty.
+ we use this to simulate device removal */
+ FsReleaseCache(sb);
+ invalidate_bdev(bdev);
+ }
+ }
+
+ kill_block_super(sb);
+}
+#endif
+
+static struct file_system_type exfat_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "exfat",
+ .mount = exfat_fs_mount,
+#ifdef CONFIG_EXFAT_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 0000000..9063f8b
--- /dev/null
+++ b/drivers/staging/exfat/exfat_super.h
@@ -0,0 +1,153 @@
+/* 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_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 /* ignore error and continue */
+#define EXFAT_ERRORS_PANIC 2 /* panic on error */
+#define EXFAT_ERRORS_RO 3 /* remount r/o on error */
+
+#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; /* permission for setting the [am]time */
+ unsigned short codepage; /* codepage for shortname conversions */
+ char *iocharset; /* charset for filename input/display */
+ unsigned char casesensitive;
+ unsigned char errors; /* on error: continue, panic, remount-ro */
+#ifdef CONFIG_EXFAT_DISCARD
+ unsigned char discard; /* flag on if -o dicard specified and device support 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;
+
+ int s_dirt;
+ struct mutex s_lock;
+ struct nls_table *nls_disk; /* Codepage used on disk */
+ struct nls_table *nls_io; /* Charset used for input and display */
+
+ struct inode *fat_inode;
+
+ spinlock_t inode_hash_lock;
+ struct hlist_head inode_hashtable[EXFAT_HASH_SIZE];
+#ifdef CONFIG_EXFAT_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;
+ struct rw_semaphore truncate_lock;
+ 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);
+}
+
+/*
+ * If ->i_mode can't hold S_IWUGO (i.e. ATTR_RO), we use ->i_attrs to
+ * save ATTR_RO instead of ->i_mode.
+ *
+ * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only
+ * bit, it's just used as flag for app.
+ */
+static inline int exfat_mode_can_hold_ro(struct inode *inode)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+
+ if (S_ISDIR(inode->i_mode))
+ return 0;
+
+ if ((~sbi->options.fs_fmask) & S_IWUGO)
+ return 1;
+ return 0;
+}
+
+/* Convert attribute bits and a mask to the UNIX mode. */
+static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi,
+ 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;
+}
+
+/* Return the FAT attribute byte for this inode */
+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 0000000..b3ccffa
--- /dev/null
+++ b/drivers/staging/exfat/exfat_upcase.c
@@ -0,0 +1,389 @@
+/*
+ * 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_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 0000000..ab6ce18
--- /dev/null
+++ b/drivers/staging/exfat/exfat_version.h
@@ -0,0 +1 @@
+#define EXFAT_VERSION "1.2.4"
--
1.8.1.2
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/