2013-08-30 10:53:15

by Benjamin Valentin

[permalink] [raw]
Subject: [PATCH] add exFAT driver

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 <[email protected]>
---
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([email protected])
+ */
+
+/*
+ * 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([email protected])
+ */
+
+/*
+ * 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([email protected])
+ */
+
+/*
+ * 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


2013-08-30 11:14:59

by Viacheslav Dubeyko

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

On Fri, 2013-08-30 at 12:46 +0200, Benjamin Valentin wrote:
> 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 <[email protected]>

Why original authors don't participate in submission of driver?

Anyway, it makes sense to prepare patch set instead of one huge patch.
Could you, please, prepare patch set?

And you should add linux-fsdevel list in CC for any discussion about
file system drivers.

Thanks,
Vyacheslav Dubeyko.

2013-08-30 15:39:41

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

On Fri, Aug 30, 2013 at 12:46:22PM +0200, Benjamin Valentin wrote:
> 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 <[email protected]>

For staging drivers I need a maintainer that is going to take the time
to shephard it into the core kernel tree. See other TODO files for
how that person is defined. Are you going to be willing to do this?

If so, can you please resend it with those changes to the TODO file, and
add a MAINTAINERS file entry?

Also, I would really like to get a signed-off-by: from the Samsung
authors for this patch, can you do that as well please?

thanks,

greg k-h

2013-09-25 16:36:38

by Benjamin Valentin

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

Am Fri, 30 Aug 2013 08:42:05 -0700
schrieb Greg Kroah-Hartman <[email protected]>:

> For staging drivers I need a maintainer that is going to take the time
> to shephard it into the core kernel tree. See other TODO files for
> how that person is defined. Are you going to be willing to do this?

I can see what I can do.

I've cleaned up the code a bit, especially where it would redefine
kernel functions. [1] Should I submit the end result, one file per
patch, or begin with the original code and submit patches on top of
that?

Also, the driver would define it's own data types like
typedef unsigned int UINT32;

I've replaced that with u32, etc as the naming implied the bit size
would matter. (And it probably does for the on-disk file system
structures, but they are used for everything) Is there any disadvantage
to this over using the standard C types?

> Also, I would really like to get a signed-off-by: from the Samsung
> authors for this patch, can you do that as well please?

I've tried reaching them by their last public e-mail addresses I could
find, but they would either bounce or I got no reply.

[1] https://github.com/benpicco/linux_exfat-staging/commits/exfat

2013-09-25 16:59:58

by Anca Emanuel

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

On Wed, Sep 25, 2013 at 7:36 PM, Benjamin Valentin
<[email protected]> wrote:
> Am Fri, 30 Aug 2013 08:42:05 -0700
> schrieb Greg Kroah-Hartman <[email protected]>:
>
>> For staging drivers I need a maintainer that is going to take the time
>> to shephard it into the core kernel tree. See other TODO files for
>> how that person is defined. Are you going to be willing to do this?
>
> I can see what I can do.
>
> I've cleaned up the code a bit, especially where it would redefine
> kernel functions. [1] Should I submit the end result, one file per
> patch, or begin with the original code and submit patches on top of
> that?

For staging code:
1. It must pass compile.
2. you have an TODO file with things to do to clean it up to the
standard quality of the kernel.

2013-09-25 17:27:08

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

On Wed, Sep 25, 2013 at 06:36:32PM +0200, Benjamin Valentin wrote:
> Am Fri, 30 Aug 2013 08:42:05 -0700
> schrieb Greg Kroah-Hartman <[email protected]>:
>
> > For staging drivers I need a maintainer that is going to take the time
> > to shephard it into the core kernel tree. See other TODO files for
> > how that person is defined. Are you going to be willing to do this?
>
> I can see what I can do.

Great.

> I've cleaned up the code a bit, especially where it would redefine
> kernel functions. [1] Should I submit the end result, one file per
> patch, or begin with the original code and submit patches on top of
> that?

Probably start with the original submission from Samsung, with their
signed-off-by: lines, and then add patches afterward that from you with
your fixes / cleanups. That shows the development effort and gives
everyone the proper credit/blame :)

> Also, the driver would define it's own data types like
> typedef unsigned int UINT32;
>
> I've replaced that with u32, etc as the naming implied the bit size
> would matter. (And it probably does for the on-disk file system
> structures, but they are used for everything) Is there any disadvantage
> to this over using the standard C types?

Those _are_ the standard kernel C types, so that's the correct thing to
do.

> > Also, I would really like to get a signed-off-by: from the Samsung
> > authors for this patch, can you do that as well please?
>
> I've tried reaching them by their last public e-mail addresses I could
> find, but they would either bounce or I got no reply.

Who did you contact at Samsung? I'll be visiting there in a week so I
can try to track some people done in person. I really want their
signed-off-by: on the patch, as it is their code to start with, and it's
a bit rude to not get their approval for the code to be merged.

thanks,

greg k-h

2013-09-25 18:32:48

by Matthew Garrett

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

On Wed, Sep 25, 2013 at 10:27:04AM -0700, Greg Kroah-Hartman wrote:

> Who did you contact at Samsung? I'll be visiting there in a week so I
> can try to track some people done in person. I really want their
> signed-off-by: on the patch, as it is their code to start with, and it's
> a bit rude to not get their approval for the code to be merged.

It's arguably rude, but it's fine under the certificate of origin.

--
Matthew Garrett | [email protected]

2013-09-25 18:45:41

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

On Wed, Sep 25, 2013 at 07:32:36PM +0100, Matthew Garrett wrote:
> On Wed, Sep 25, 2013 at 10:27:04AM -0700, Greg Kroah-Hartman wrote:
>
> > Who did you contact at Samsung? I'll be visiting there in a week so I
> > can try to track some people done in person. I really want their
> > signed-off-by: on the patch, as it is their code to start with, and it's
> > a bit rude to not get their approval for the code to be merged.
>
> It's arguably rude, but it's fine under the certificate of origin.

Oh, I totally agree, just trying to be nice here. If a company objects
to having their code included in the tree, we should always take that
into consideration, especially given the ability for others to maintain
it. We've done this for years, nothing new here at all.

thanks,

greg k-h

2013-09-25 19:29:44

by Alexander Holler

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

Am 25.09.2013 20:45, schrieb Greg Kroah-Hartman:
> On Wed, Sep 25, 2013 at 07:32:36PM +0100, Matthew Garrett wrote:
>> On Wed, Sep 25, 2013 at 10:27:04AM -0700, Greg Kroah-Hartman wrote:
>>
>>> Who did you contact at Samsung? I'll be visiting there in a week so I
>>> can try to track some people done in person. I really want their
>>> signed-off-by: on the patch, as it is their code to start with, and it's
>>> a bit rude to not get their approval for the code to be merged.
>>
>> It's arguably rude, but it's fine under the certificate of origin.
>
> Oh, I totally agree, just trying to be nice here. If a company objects
> to having their code included in the tree, we should always take that
> into consideration, especially given the ability for others to maintain
> it. We've done this for years, nothing new here at all.

Maybe a silly question, but isn't exFAT protected by some MS owned
patents which might drive Linux users into the hand of MS lawyers as
already happened with FAT? It would make me wonder if not. Maybe you
could ask Samsung about that too, when you are there.

Regards,

Alexander Holler

2013-09-25 20:21:54

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

On Wed, Sep 25, 2013 at 09:28:56PM +0200, Alexander Holler wrote:
> Am 25.09.2013 20:45, schrieb Greg Kroah-Hartman:
> > On Wed, Sep 25, 2013 at 07:32:36PM +0100, Matthew Garrett wrote:
> >> On Wed, Sep 25, 2013 at 10:27:04AM -0700, Greg Kroah-Hartman wrote:
> >>
> >>> Who did you contact at Samsung? I'll be visiting there in a week so I
> >>> can try to track some people done in person. I really want their
> >>> signed-off-by: on the patch, as it is their code to start with, and it's
> >>> a bit rude to not get their approval for the code to be merged.
> >>
> >> It's arguably rude, but it's fine under the certificate of origin.
> >
> > Oh, I totally agree, just trying to be nice here. If a company objects
> > to having their code included in the tree, we should always take that
> > into consideration, especially given the ability for others to maintain
> > it. We've done this for years, nothing new here at all.
>
> Maybe a silly question, but isn't exFAT protected by some MS owned
> patents which might drive Linux users into the hand of MS lawyers as
> already happened with FAT? It would make me wonder if not. Maybe you
> could ask Samsung about that too, when you are there.

Because Samsung released the code under the GPLv2, and their lawyers
understand what that means, should answer any question you might have
about this.

thanks,

greg k-h

2013-09-25 20:44:12

by Alexander Holler

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

Am 25.09.2013 22:21, schrieb Greg Kroah-Hartman:
> On Wed, Sep 25, 2013 at 09:28:56PM +0200, Alexander Holler wrote:

>> Maybe a silly question, but isn't exFAT protected by some MS owned
>> patents which might drive Linux users into the hand of MS lawyers as
>> already happened with FAT? It would make me wonder if not. Maybe you
>> could ask Samsung about that too, when you are there.
>
> Because Samsung released the code under the GPLv2, and their lawyers
> understand what that means, should answer any question you might have
> about this.
>
> thanks,

I'll have to thank for the clarification.

Alexander Holler

2013-09-25 21:44:23

by Anton Altaparmakov

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

Hi,

On 25 Sep 2013, at 21:21, Greg Kroah-Hartman <[email protected]> wrote:
> On Wed, Sep 25, 2013 at 09:28:56PM +0200, Alexander Holler wrote:
>>
>> Maybe a silly question, but isn't exFAT protected by some MS owned
>> patents which might drive Linux users into the hand of MS lawyers as
>> already happened with FAT?

Yes, it is. You cannot use exFAT without a Microsoft patent license (unless you live in countries without software patents perhaps).

>> It would make me wonder if not. Maybe you could ask Samsung about that too, when you are there.
>
> Because Samsung released the code under the GPLv2, and their lawyers
> understand what that means, should answer any question you might have
> about this.

Sorry but you have no idea what you are talking about. Samsung modified the GPL-ed FAT driver to make it work with exFAT. Therefore their exFAT driver was GPL as a derived work. They got caught and had to release the source code.

It has NOTHING to do with what their lawyers understand, etc, and if you are really going to be visiting Samsung and if they are willing to talk to you about it you will be retracting what you wrote above in a hurry... Sorry I cannot say more but I strongly suggest NOT to use this "GPL-ed exFAT driver" under any circumstances unless you get a patent license from Microsoft first.

Best regards,

Anton

> thanks,
>
> greg k-h

--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge
J.J. Thomson Avenue, Cambridge, CB3 0RB, UK

2013-09-25 21:57:27

by Benjamin Valentin

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

Am Wed, 25 Sep 2013 10:27:04 -0700
schrieb Greg Kroah-Hartman <[email protected]>:

> > > Also, I would really like to get a signed-off-by: from the Samsung
> > > authors for this patch, can you do that as well please?
> >
> > I've tried reaching them by their last public e-mail addresses I
> > could find, but they would either bounce or I got no reply.
>
> Who did you contact at Samsung? I'll be visiting there in a week so I
> can try to track some people done in person. I really want their
> signed-off-by: on the patch, as it is their code to start with, and
> it's a bit rude to not get their approval for the code to be merged.

I didn't really know who to contact at Samsung, so I've searched the web
for Joosun Hahn and Sung-Kwan Kim. They both had addresses from the
Memory & Storage Architecture Lab at Seoul National University, but
they seem no longer active.
I've also tried the inquiry form on the Samsung web page, but got no
reply.

It'd be great if you could get Samsung to work on this too, I guess
they weren't originally so keen on mainlining it when it took the
Software Freedom Conservancy to get them to release the code [1],
but now that it's out in the open they might as well take the benefits
from mainline inclusion.

[1] http://sfconservancy.org/news/2013/aug/16/exfat-samsung/

2013-09-25 22:02:14

by Alexander Holler

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

Am 25.09.2013 23:44, schrieb Anton Altaparmakov:
> Hi,
>
> On 25 Sep 2013, at 21:21, Greg Kroah-Hartman <[email protected]> wrote:
>> On Wed, Sep 25, 2013 at 09:28:56PM +0200, Alexander Holler wrote:
>>>
>>> Maybe a silly question, but isn't exFAT protected by some MS owned
>>> patents which might drive Linux users into the hand of MS lawyers as
>>> already happened with FAT?
>
> Yes, it is. You cannot use exFAT without a Microsoft patent license (unless you live in countries without software patents perhaps).

That part about software patents is a farce. Here in Germany (and as I
understood in whole Europe), software patents should not exist. But the
FAT patent got approved by our Bungesgerichtshof, which is something
like the Federal Supreme court in the US.

Regards,

Alexander Holler

2013-09-25 22:10:09

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

On Wed, Sep 25, 2013 at 10:44:15PM +0100, Anton Altaparmakov wrote:
> Hi,
>
> On 25 Sep 2013, at 21:21, Greg Kroah-Hartman <[email protected]> wrote:
> > On Wed, Sep 25, 2013 at 09:28:56PM +0200, Alexander Holler wrote:
> >>
> >> Maybe a silly question, but isn't exFAT protected by some MS owned
> >> patents which might drive Linux users into the hand of MS lawyers as
> >> already happened with FAT?
>
> Yes, it is. You cannot use exFAT without a Microsoft patent license
> (unless you live in countries without software patents perhaps).

Given that you that you are not a Microsoft representative, nor a
Samsung employee, I don't understand how you can make such a definitive
statement.

> >> It would make me wonder if not. Maybe you could ask Samsung about
> >> that too, when you are there.
> >
> > Because Samsung released the code under the GPLv2, and their lawyers
> > understand what that means, should answer any question you might have
> > about this.
>
> Sorry but you have no idea what you are talking about.

Ah, that's a lovely way to engage in a conversation.

> Samsung modified the GPL-ed FAT driver to make it work with exFAT.
> Therefore their exFAT driver was GPL as a derived work. They got
> caught and had to release the source code.

And now you claim to be a Samsung representative again, I think your
country has some bad liable laws you might wish to watch out for...

This isn't going to go very far, so I'll just not respond anymore, it's
not going to be productive, and given that I don't see your name on the
code here, I don't see why I need to.

Please stick to technical discussions about the code on the kernel
mailing lists. Legal discussions can be left up to the lawyers, of
which we are not.

greg k-h

2013-09-25 22:11:51

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

On Wed, Sep 25, 2013 at 11:57:21PM +0200, Benjamin Valentin wrote:
> Am Wed, 25 Sep 2013 10:27:04 -0700
> schrieb Greg Kroah-Hartman <[email protected]>:
>
> > > > Also, I would really like to get a signed-off-by: from the Samsung
> > > > authors for this patch, can you do that as well please?
> > >
> > > I've tried reaching them by their last public e-mail addresses I
> > > could find, but they would either bounce or I got no reply.
> >
> > Who did you contact at Samsung? I'll be visiting there in a week so I
> > can try to track some people done in person. I really want their
> > signed-off-by: on the patch, as it is their code to start with, and
> > it's a bit rude to not get their approval for the code to be merged.
>
> I didn't really know who to contact at Samsung, so I've searched the web
> for Joosun Hahn and Sung-Kwan Kim. They both had addresses from the
> Memory & Storage Architecture Lab at Seoul National University, but
> they seem no longer active.
> I've also tried the inquiry form on the Samsung web page, but got no
> reply.

Ok, I'll try to track the people responsible for this down when I visit.
In the mean time, you can continue to work on cleaning up the code :)

thanks,

greg k-h

2013-09-25 22:30:18

by Alexander Holler

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

Am 26.09.2013 00:10, schrieb Greg Kroah-Hartman:

> Please stick to technical discussions about the code on the kernel
> mailing lists. Legal discussions can be left up to the lawyers, of
> which we are not.

Hmm, but I would like to know if someone has to fear getting owned by
Microsoft if he would use that driver.

Giving the rumours about Linux companies having to pay Microsoft and
giving the fact that all of those licencees seem to don't have to speak
about what Microsoft claims patents for and for what they have to pay, I
obviously think adding that driver to Linux and thus making exFAT more
general accepted is a very bad idea.

Of course, I'm not a lawyer too, but as a responsible Linux developer, I
should at least be able to warn other parities when they approach me and
want to use exFAT. Doing such without the maybe necessary license might
drive small companies into the ground because most of them are unable to
even think about having the money needed to talk with Microsoft lawyers
in front of a court.

Regards,

Alexander Holler

2013-09-25 22:53:14

by Matthew Garrett

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

On Thu, Sep 26, 2013 at 12:29:03AM +0200, Alexander Holler wrote:
> Am 26.09.2013 00:10, schrieb Greg Kroah-Hartman:
>
> > Please stick to technical discussions about the code on the kernel
> > mailing lists. Legal discussions can be left up to the lawyers, of
> > which we are not.
>
> Hmm, but I would like to know if someone has to fear getting owned by
> Microsoft if he would use that driver.

Nobody should ever assume that any piece of code they receive is free of
third party patent claims, no matter where the code came from.

--
Matthew Garrett | [email protected]

2013-09-26 08:11:32

by Anton Altaparmakov

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

Hi,

On 25 Sep 2013, at 23:10, Greg Kroah-Hartman <[email protected]> wrote:
> On Wed, Sep 25, 2013 at 10:44:15PM +0100, Anton Altaparmakov wrote:
>> On 25 Sep 2013, at 21:21, Greg Kroah-Hartman <[email protected]> wrote:
>>> On Wed, Sep 25, 2013 at 09:28:56PM +0200, Alexander Holler wrote:
>>>>
>>>> Maybe a silly question, but isn't exFAT protected by some MS owned
>>>> patents which might drive Linux users into the hand of MS lawyers as
>>>> already happened with FAT?
>>
>> Yes, it is. You cannot use exFAT without a Microsoft patent license
>> (unless you live in countries without software patents perhaps).
>
> Given that you that you are not a Microsoft representative, nor a
> Samsung employee, I don't understand how you can make such a definitive
> statement.

Have you actually read the source code that was released?

May I quote just one bit:

from exfat_1.2.4/exfat.c (available from http://opensource.samsung.com - just search for exfat) at the top:

/* 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([email protected])
*/

That is somewhat conclusively a derivative work is it not?

Also, have a read of this article:

http://www.phoronix.com/scan.php?page=news_item&px=MTQzODQ

Which explains further who made them open source it after they saw the leaked code on github.

So even without resorting to knowledge I may not discuss it is pretty conclusive that what I said is correct as anyone driving google can find for themselves as I pointed out above...

>>>> It would make me wonder if not. Maybe you could ask Samsung about
>>>> that too, when you are there.
>>>
>>> Because Samsung released the code under the GPLv2, and their lawyers
>>> understand what that means, should answer any question you might have
>>> about this.
>>
>> Sorry but you have no idea what you are talking about.
>
> Ah, that's a lovely way to engage in a conversation.

I did say sorry! (-;

>> Samsung modified the GPL-ed FAT driver to make it work with exFAT.
>> Therefore their exFAT driver was GPL as a derived work. They got
>> caught and had to release the source code.
>
> And now you claim to be a Samsung representative again, I think your
> country has some bad liable laws you might wish to watch out for...

I am not claiming anything and least of all to be representing Samsung!!!

Libel implies untruth and as you can see above I am only stating what anyone can readily find on google.

> This isn't going to go very far, so I'll just not respond anymore, it's
> not going to be productive, and given that I don't see your name on the
> code here, I don't see why I need to.
>
> Please stick to technical discussions about the code on the kernel
> mailing lists. Legal discussions can be left up to the lawyers, of
> which we are not.


I agree, but then please stop making public assertions that people can use the exfat driver legally. You just yourself said you are not a lawyer so I do not understand how you can make your assertion!

If anyone cares, here is Microsoft's exFAT licensing page which I strongly recommend you read before you use that driver:

http://www.microsoft.com/en-us/legal/intellectualproperty/IPLicensing/Programs/exFATFileSystem.aspx

Also if you search google for "exFAT patent" you can find some that way but there are also others that are not found that way but that are clearly essential for any exFAT implementation (according to the technical review I did of them). I am not sure whether I am allowed to give a list or not so I will refrain from doing so.

Best regards,

Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge
J.J. Thomson Avenue, Cambridge, CB3 0RB, UK

2013-09-26 08:13:30

by Anton Altaparmakov

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

Hi,

On 25 Sep 2013, at 23:29, Alexander Holler <[email protected]> wrote:
> Am 26.09.2013 00:10, schrieb Greg Kroah-Hartman:
>> Please stick to technical discussions about the code on the kernel
>> mailing lists. Legal discussions can be left up to the lawyers, of
>> which we are not.
>
> Hmm, but I would like to know if someone has to fear getting owned by
> Microsoft if he would use that driver.
>
> Giving the rumours about Linux companies having to pay Microsoft and
> giving the fact that all of those licencees seem to don't have to speak
> about what Microsoft claims patents for and for what they have to pay, I
> obviously think adding that driver to Linux and thus making exFAT more
> general accepted is a very bad idea.
>
> Of course, I'm not a lawyer too, but as a responsible Linux developer, I
> should at least be able to warn other parities when they approach me and
> want to use exFAT. Doing such without the maybe necessary license might
> drive small companies into the ground because most of them are unable to
> even think about having the money needed to talk with Microsoft lawyers
> in front of a court.


Exactly. That is all I was trying to do. Warn people/companies not to use the driver because they may get sued for using it. As the below Microsoft exFAT licensing page says at the bottom:

<quote>
Please note that open source or other publicly available implementations of exFAT do not include an IP license from Microsoft. For licensing information, please contact [email protected].
</quote>

Above is from bottom of:

http://www.microsoft.com/en-us/legal/intellectualproperty/IPLicensing/Programs/exFATFileSystem.aspx

Best regards,

Anton
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge
J.J. Thomson Avenue, Cambridge, CB3 0RB, UK

2013-09-26 10:41:48

by Dan Carpenter

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

Anton, I already told you politely in a private email that you are being
offtopic. This is professional email list for programmers. Most of us
are forbidden to discuss laws except with our corporate legal dept.
None of us are lawyers. We're not qualified to read any of your links
or interpret how they apply to anyone. We can't help you or respond to
you.

In other words, shut the fuck up. Let's not continue this thread.

regards,
dan carpenter

2013-09-26 10:49:20

by Alexander Holler

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

Am 26.09.2013 12:41, schrieb Dan Carpenter:
> Anton, I already told you politely in a private email that you are being
> offtopic. This is professional email list for programmers. Most of us
> are forbidden to discuss laws except with our corporate legal dept.
> None of us are lawyers. We're not qualified to read any of your links
> or interpret how they apply to anyone. We can't help you or respond to
> you.
>
> In other words, shut the fuck up. Let's not continue this thread.

First my first name is Alexander and not Anton, and second I haven't
respond to any mail on that topic after I received your private mail.

And third, "the fuck" just describes the topic perfectly.

Regards,

Alexander Holler

2013-09-26 11:07:21

by Dan Carpenter

[permalink] [raw]
Subject: Re: [PATCH] add exFAT driver

On Thu, Sep 26, 2013 at 12:48:35PM +0200, Alexander Holler wrote:
> Am 26.09.2013 12:41, schrieb Dan Carpenter:
> >Anton, I already told you politely in a private email that you are being
> >offtopic. This is professional email list for programmers. Most of us
> >are forbidden to discuss laws except with our corporate legal dept.
> >None of us are lawyers. We're not qualified to read any of your links
> >or interpret how they apply to anyone. We can't help you or respond to
> >you.
> >
> >In other words, shut the fuck up. Let's not continue this thread.
>
> First my first name is Alexander and not Anton, and second I haven't
> respond to any mail on that topic after I received your private
> mail.

No, Alexander, you're cool. The email was meant specifically for Aton.
:P

regards,
dan carpenter