diff options
| author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-11-19 15:18:53 -0800 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-11-19 15:18:53 -0800 |
| commit | d664bd7bf73c4513b3e35d4a49298a85833f7fec (patch) | |
| tree | 9e622d513c09fe3c35cfda2602c5bd844d1c41dd | |
| parent | cbff5e596f912b830138e1126227092450a84055 (diff) | |
| parent | 18b6545278f3189229022380e7418c905ffa3ff3 (diff) | |
| download | patches-d664bd7bf73c4513b3e35d4a49298a85833f7fec.tar.gz | |
Merge branch 'master' of ra.kernel.org:/pub/scm/linux/kernel/git/gregkh/patches
| -rw-r--r-- | d01.patch | 14 | ||||
| -rw-r--r-- | pci-msi-fix.patch | 130 | ||||
| -rw-r--r-- | series | 5 | ||||
| -rw-r--r-- | staging-exfat-add-filesystem-to-drivers-staging-exfat.patch | 11166 |
4 files changed, 11308 insertions, 7 deletions
diff --git a/d01.patch b/d01.patch index 1361d94b2c4744..d47deca4903090 100644 --- a/d01.patch +++ b/d01.patch @@ -12,7 +12,7 @@ --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c -@@ -1890,7 +1890,7 @@ struct hid_dynid { +@@ -1898,7 +1898,7 @@ struct hid_dynid { * Adds a new dynamic hid device ID to this driver, * and causes the driver to probe for all devices again. */ @@ -21,7 +21,7 @@ size_t count) { struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); -@@ -1922,7 +1922,13 @@ static ssize_t store_new_id(struct devic +@@ -1930,7 +1930,13 @@ static ssize_t store_new_id(struct devic return ret ? : count; } @@ -36,7 +36,7 @@ static void hid_free_dynids(struct hid_driver *hdrv) { -@@ -2082,6 +2088,7 @@ static int hid_uevent(struct device *dev +@@ -2090,6 +2096,7 @@ static int hid_uevent(struct device *dev static struct bus_type hid_bus_type = { .name = "hid", .dev_groups = hid_dev_groups, @@ -44,7 +44,7 @@ .match = hid_bus_match, .probe = hid_device_probe, .remove = hid_device_remove, -@@ -2528,8 +2535,6 @@ EXPORT_SYMBOL_GPL(hid_destroy_device); +@@ -2527,8 +2534,6 @@ EXPORT_SYMBOL_GPL(hid_destroy_device); int __hid_register_driver(struct hid_driver *hdrv, struct module *owner, const char *mod_name) { @@ -53,7 +53,7 @@ hdrv->driver.name = hdrv->name; hdrv->driver.bus = &hid_bus_type; hdrv->driver.owner = owner; -@@ -2538,21 +2543,12 @@ int __hid_register_driver(struct hid_dri +@@ -2537,21 +2542,12 @@ int __hid_register_driver(struct hid_dri INIT_LIST_HEAD(&hdrv->dyn_list); spin_lock_init(&hdrv->dyn_lock); @@ -355,7 +355,7 @@ { --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c -@@ -5376,50 +5376,43 @@ static struct pci_driver megasas_pci_dri +@@ -5371,50 +5371,43 @@ static struct pci_driver megasas_pci_dri /* * Sysfs driver attributes */ @@ -417,7 +417,7 @@ { int retval = count; if(sscanf(buf,"%u",&megasas_dbg_lvl)<1){ -@@ -5429,8 +5422,7 @@ megasas_sysfs_set_dbg_lvl(struct device_ +@@ -5424,8 +5417,7 @@ megasas_sysfs_set_dbg_lvl(struct device_ return retval; } diff --git a/pci-msi-fix.patch b/pci-msi-fix.patch new file mode 100644 index 00000000000000..5f000ccc930b5c --- /dev/null +++ b/pci-msi-fix.patch @@ -0,0 +1,130 @@ +--- + drivers/pci/msi.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + include/linux/pci.h | 1 + 2 files changed, 86 insertions(+) + +--- a/drivers/pci/msi.c ++++ b/drivers/pci/msi.c +@@ -363,6 +363,9 @@ void write_msi_msg(unsigned int irq, str + static void free_msi_irqs(struct pci_dev *dev) + { + struct msi_desc *entry, *tmp; ++ struct attribute **msi_attrs; ++ struct device_attribute *dev_attr; ++ int count = 0; + + list_for_each_entry(entry, &dev->msi_list, list) { + int i, nvec; +@@ -398,6 +401,22 @@ static void free_msi_irqs(struct pci_dev + list_del(&entry->list); + kfree(entry); + } ++ ++ if (dev->msi_irq_groups) { ++ sysfs_remove_groups(&dev->dev.kobj, dev->msi_irq_groups); ++ msi_attrs = dev->msi_irq_groups[0]->attrs; ++ list_for_each_entry(entry, &dev->msi_list, list) { ++ dev_attr = container_of(msi_attrs[count], ++ struct device_attribute, attr); ++ kfree(dev_attr->attr.name); ++ kfree(dev_attr); ++ ++count; ++ } ++ kfree(msi_attrs); ++ kfree(dev->msi_irq_groups[0]); ++ kfree(dev->msi_irq_groups); ++ dev->msi_irq_groups = NULL; ++ } + } + + static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) +@@ -527,13 +546,79 @@ static struct kobj_type msi_irq_ktype = + .default_attrs = msi_irq_default_attrs, + }; + ++static ssize_t msi_mode_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct pci_dev *pdev = to_pci_dev(dev); ++ struct msi_desc *entry; ++ unsigned long irq; ++ int retval; ++ ++ retval = kstrtoul(attr->attr.name, 10, &irq); ++ if (retval) ++ return retval; ++ ++ list_for_each_entry(entry, &pdev->msi_list, list) { ++ if (entry->irq == irq) { ++ return sprintf(buf, "%s\n", ++ entry->msi_attrib.is_msix ? "msix" : "msi"); ++ } ++ } ++ return -ENODEV; ++} ++ + static int populate_msi_sysfs(struct pci_dev *pdev) + { ++ struct attribute **msi_attrs; ++ struct device_attribute *msi_dev_attr; ++ struct attribute_group *msi_irq_group; ++ const struct attribute_group **msi_irq_groups; + struct msi_desc *entry; + struct kobject *kobj; + int ret; ++ int num_msi = 0; + int count = 0; + ++ /* Determine how many msi entries we have */ ++ list_for_each_entry(entry, &pdev->msi_list, list) { ++ ++num_msi; ++ } ++ if (!num_msi) ++ return 0; ++ ++ /* Dynamically create the MSI attributes for the PCI device */ ++ msi_attrs = kzalloc(sizeof(void *) * (num_msi + 1), GFP_KERNEL); ++ if (!msi_attrs) ++ return -ENOMEM; ++ list_for_each_entry(entry, &pdev->msi_list, list) { ++ char *name = kmalloc(20, GFP_KERNEL); ++ msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL); ++ if (!msi_dev_attr) ++ return -ENOMEM; ++ sprintf(name, "%d", entry->irq); ++ msi_dev_attr->attr.name = name; ++ msi_dev_attr->attr.mode = S_IRUGO; ++ msi_dev_attr->show = msi_mode_show; ++ msi_attrs[count] = &msi_dev_attr->attr; ++ ++count; ++ } ++ ++ msi_irq_group = kzalloc(sizeof(*msi_irq_group), GFP_KERNEL); ++ if (!msi_irq_group) ++ return -ENOMEM; ++ msi_irq_group->name = "msi_irqs_2"; ++ msi_irq_group->attrs = msi_attrs; ++ ++ msi_irq_groups = kzalloc(sizeof(void *) * 2, GFP_KERNEL); ++ if (!msi_irq_groups) ++ return -ENOMEM; ++ msi_irq_groups[0] = msi_irq_group; ++ ++ ret = sysfs_create_groups(&pdev->dev.kobj, msi_irq_groups); ++ if (ret) ++ return ret; ++ pdev->msi_irq_groups = msi_irq_groups; ++ + pdev->msi_kset = kset_create_and_add("msi_irqs", NULL, &pdev->dev.kobj); + if (!pdev->msi_kset) + return -ENOMEM; +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -352,6 +352,7 @@ struct pci_dev { + #ifdef CONFIG_PCI_MSI + struct list_head msi_list; + struct kset *msi_kset; ++ const struct attribute_group **msi_irq_groups; + #endif + struct pci_vpd *vpd; + #ifdef CONFIG_PCI_ATS @@ -1,3 +1,8 @@ +staging-exfat-add-filesystem-to-drivers-staging-exfat.patch + + + +pci-msi-fix.patch xen-disable-clock-timer-when-shutting-down.patch # patches already in my git trees, but still here so I don't loose them. diff --git a/staging-exfat-add-filesystem-to-drivers-staging-exfat.patch b/staging-exfat-add-filesystem-to-drivers-staging-exfat.patch new file mode 100644 index 00000000000000..137f8ae908ee4d --- /dev/null +++ b/staging-exfat-add-filesystem-to-drivers-staging-exfat.patch @@ -0,0 +1,11166 @@ +Date: Fri, 16 Aug 2013 12:52:00 -0800 +Subject: staging: exfat: add filesystem to drivers/staging/exfat + +From: Joe Kim <someone@samsung.com> + +This is the exfat code version 1.2.4 as found on the +http://opensource.samsung.com site in the zip file exfat_1.2.4.zip with +the contents: +$ sha256sum exfat_1.2.4.zip +4cb39c4f44b7a68cc391596384744a32f948ba659697542ff771764b215810f3 exfat_1.2.4.zip + +Released under the GNU General Public License, version 2, as specified +from that web site. + +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/exfat/Makefile | 13 + + drivers/staging/exfat/README | 21 + + drivers/staging/exfat/exfat.c | 4950 +++++++++++++++++++++++++++++++++ + drivers/staging/exfat/exfat.h | 598 ++++ + drivers/staging/exfat/exfat_api.c | 457 +++ + drivers/staging/exfat/exfat_api.h | 165 ++ + drivers/staging/exfat/exfat_blkdev.c | 156 ++ + drivers/staging/exfat/exfat_blkdev.h | 46 + + drivers/staging/exfat/exfat_cache.c | 740 +++++ + drivers/staging/exfat/exfat_cache.h | 63 + + drivers/staging/exfat/exfat_config.h | 57 + + drivers/staging/exfat/exfat_data.c | 40 + + drivers/staging/exfat/exfat_data.h | 39 + + drivers/staging/exfat/exfat_global.c | 119 + + drivers/staging/exfat/exfat_global.h | 127 + + drivers/staging/exfat/exfat_nls.c | 347 +++ + drivers/staging/exfat/exfat_nls.h | 68 + + drivers/staging/exfat/exfat_oal.c | 146 + + drivers/staging/exfat/exfat_oal.h | 53 + + drivers/staging/exfat/exfat_part.h | 54 + + drivers/staging/exfat/exfat_super.c | 2154 ++++++++++++++ + drivers/staging/exfat/exfat_super.h | 149 + + drivers/staging/exfat/exfat_upcase.c | 390 +++ + drivers/staging/exfat/exfat_version.h | 1 + + 24 files changed, 10953 insertions(+) + create mode 100644 drivers/staging/exfat/Makefile + create mode 100644 drivers/staging/exfat/README + create mode 100644 drivers/staging/exfat/exfat.c + create mode 100644 drivers/staging/exfat/exfat.h + create mode 100644 drivers/staging/exfat/exfat_api.c + create mode 100644 drivers/staging/exfat/exfat_api.h + create mode 100644 drivers/staging/exfat/exfat_blkdev.c + create mode 100644 drivers/staging/exfat/exfat_blkdev.h + create mode 100644 drivers/staging/exfat/exfat_cache.c + create mode 100644 drivers/staging/exfat/exfat_cache.h + create mode 100644 drivers/staging/exfat/exfat_config.h + create mode 100644 drivers/staging/exfat/exfat_data.c + create mode 100644 drivers/staging/exfat/exfat_data.h + create mode 100644 drivers/staging/exfat/exfat_global.c + create mode 100644 drivers/staging/exfat/exfat_global.h + create mode 100644 drivers/staging/exfat/exfat_nls.c + create mode 100644 drivers/staging/exfat/exfat_nls.h + create mode 100644 drivers/staging/exfat/exfat_oal.c + create mode 100644 drivers/staging/exfat/exfat_oal.h + create mode 100644 drivers/staging/exfat/exfat_part.h + create mode 100644 drivers/staging/exfat/exfat_super.c + create mode 100644 drivers/staging/exfat/exfat_super.h + create mode 100644 drivers/staging/exfat/exfat_upcase.c + create mode 100644 drivers/staging/exfat/exfat_version.h + +diff --git a/drivers/staging/exfat/Makefile b/drivers/staging/exfat/Makefile +new file mode 100644 +index 00000000..5ff84c8d +--- /dev/null ++++ b/drivers/staging/exfat/Makefile +@@ -0,0 +1,13 @@ ++obj-m += exfat_core.o exfat_fs.o ++ ++exfat_fs-y := exfat_super.o ++ ++exfat_core-y := exfat.o exfat_api.o exfat_blkdev.o exfat_cache.o \ ++ exfat_data.o exfat_global.o exfat_nls.o exfat_oal.o exfat_upcase.o ++ ++all: ++ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules ++ ++clean: ++ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean ++ +diff --git a/drivers/staging/exfat/README b/drivers/staging/exfat/README +new file mode 100644 +index 00000000..32a1f5f3 +--- /dev/null ++++ b/drivers/staging/exfat/README +@@ -0,0 +1,21 @@ ++HOW TO BUILD YOUR KERNEL WITH THE exFAT FILESYSTEM DRIVER ++ ++- get Kernel code of your Samsung Product in OSRC website. (http://opensource.samsung.com/) ++ ++- make "exfat" directory into {Kernel}/drivers/ ++ ++- copy exFAT source code into {Kernel}/drivers/exfat/ ++ ++- edit default kernel configuration file of your device. ++$ vi {Kernel}/arch/arm/configs/{your_device_defconfig} ++#NLS Setting ++CONFIG_NLS_UTF8=y ++ ++- edit a Makefile in Kernel/drivers ++$ vi {Kernel}/drivers/Makefile ++obj-y += exfat/ ++ ++- execute command to make for your Kernel. (see README_kernel.txt in Kernel) ++ ex) make arch=arm defconfig ++ make ++ +diff --git a/drivers/staging/exfat/exfat.c b/drivers/staging/exfat/exfat.c +new file mode 100644 +index 00000000..c7af0e7d +--- /dev/null ++++ b/drivers/staging/exfat/exfat.c +@@ -0,0 +1,4950 @@ ++/* Some of the source code in this file came from "linux/fs/fat/misc.c". */ ++/* ++ * linux/fs/fat/misc.c ++ * ++ * Written 1992,1993 by Werner Almesberger ++ * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 ++ * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) ++ */ ++ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include <linux/version.h> ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++#include "exfat_data.h" ++#include "exfat_oal.h" ++ ++#include "exfat_blkdev.h" ++#include "exfat_cache.h" ++#include "exfat_nls.h" ++#include "exfat_api.h" ++#include "exfat_super.h" ++#include "exfat.h" ++ ++#include <linux/blkdev.h> ++ ++#define THERE_IS_MBR 0 ++ ++#if (THERE_IS_MBR == 1) ++#include "exfat_part.h" ++#endif ++ ++#define DELAYED_SYNC 0 ++ ++#define ELAPSED_TIME 0 ++ ++#if (ELAPSED_TIME == 1) ++#include <linux/time.h> ++ ++static UINT32 __t1, __t2; ++static UINT32 get_current_msec(void) ++{ ++ struct timeval tm; ++ do_gettimeofday(&tm); ++ return (UINT32)(tm.tv_sec*1000000 + tm.tv_usec); ++} ++#define TIME_START() do {__t1 = get_current_msec(); } while (0) ++#define TIME_END() do {__t2 = get_current_msec(); } while (0) ++#define PRINT_TIME(n) do {printk("[EXFAT] Elapsed time %d = %d (usec)\n", n, (__t2 - __t1)); } while (0) ++#else ++#define TIME_START() ++#define TIME_END() ++#define PRINT_TIME(n) ++#endif ++ ++static void __set_sb_dirty(struct super_block *sb) ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) ++ sb->s_dirt = 1; ++#else ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ sbi->s_dirt = 1; ++#endif ++} ++ ++extern UINT8 uni_upcase[]; ++ ++static UINT8 name_buf[MAX_PATH_LENGTH *MAX_CHARSET_SIZE]; ++ ++static INT8 *reserved_names[] = { ++ "AUX ", "CON ", "NUL ", "PRN ", ++ "COM1 ", "COM2 ", "COM3 ", "COM4 ", ++ "COM5 ", "COM6 ", "COM7 ", "COM8 ", "COM9 ", ++ "LPT1 ", "LPT2 ", "LPT3 ", "LPT4 ", ++ "LPT5 ", "LPT6 ", "LPT7 ", "LPT8 ", "LPT9 ", ++ NULL ++}; ++ ++static UINT8 free_bit[] = { ++ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, ++ 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, ++ 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, ++ 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, ++ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, ++ 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, ++ 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, ++ 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, ++ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, ++ 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, ++ 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, ++ 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, ++ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 ++}; ++ ++static UINT8 used_bit[] = { ++ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, ++ 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, ++ 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, ++ 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, ++ 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, ++ 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, ++ 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, ++ 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, ++ 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, ++ 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, ++ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 ++}; ++ ++INT32 ffsInit(void) ++{ ++ INT32 ret; ++ ++ ret = bdev_init(); ++ if (ret) ++ return ret; ++ ++ ret = fs_init(); ++ if (ret) ++ return ret; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsShutdown(void) ++{ ++ INT32 ret; ++ ret = fs_shutdown(); ++ if (ret) ++ return ret; ++ ++ ret = bdev_shutdown(); ++ if (ret) ++ return ret; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsMountVol(struct super_block *sb, INT32 drv) ++{ ++ INT32 i, ret; ++#if (THERE_IS_MBR == 1) ++ MBR_SECTOR_T *p_mbr; ++ PART_ENTRY_T *p_pte; ++#endif ++ PBR_SECTOR_T *p_pbr; ++ struct buffer_head *tmp_bh = NULL; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ printk("[EXFAT] trying to mount...\n"); ++ ++ p_fs->drv = drv; ++ p_fs->dev_ejected = FALSE; ++ ++ if (bdev_open(sb)) ++ return FFS_MEDIAERR; ++ ++ if (p_bd->sector_size < sb->s_blocksize) ++ return FFS_MEDIAERR; ++ if (p_bd->sector_size > sb->s_blocksize) ++ sb_set_blocksize(sb, p_bd->sector_size); ++ ++ if (sector_read(sb, 0, &tmp_bh, 1) != FFS_SUCCESS) ++ return FFS_MEDIAERR; ++ ++#if (THERE_IS_MBR == 1) ++ if (buf[0] != 0xEB) { ++ p_mbr = (MBR_SECTOR_T *) tmp_bh->b_data; ++ ++ if (GET16_A(p_mbr->signature) != MBR_SIGNATURE) { ++ brelse(tmp_bh); ++ bdev_close(sb); ++ return FFS_FORMATERR; ++ } ++ ++ p_pte = (PART_ENTRY_T *) p_mbr->partition + 0; ++ p_fs->PBR_sector = GET32(p_pte->start_sector); ++ p_fs->num_sectors = GET32(p_pte->num_sectors); ++ ++ if (p_fs->num_sectors == 0) { ++ brelse(tmp_bh); ++ bdev_close(sb); ++ return FFS_ERROR; ++ } ++ ++ if (sector_read(sb, p_fs->PBR_sector, &tmp_bh, 1) != FFS_SUCCESS) { ++ bdev_close(sb); ++ return FFS_MEDIAERR; ++ } ++ } else { ++#endif ++ p_fs->PBR_sector = 0; ++#if (THERE_IS_MBR == 1) ++ } ++#endif ++ ++ p_pbr = (PBR_SECTOR_T *) tmp_bh->b_data; ++ ++ if (GET16_A(p_pbr->signature) != PBR_SIGNATURE) { ++ brelse(tmp_bh); ++ bdev_close(sb); ++ return FFS_FORMATERR; ++ } ++ ++ for (i = 0; i < 53; i++) ++ if (p_pbr->bpb[i]) ++ break; ++ ++ if (i < 53) { ++ if (GET16(p_pbr->bpb+11)) ++ ret = fat16_mount(sb, p_pbr); ++ else ++ ret = fat32_mount(sb, p_pbr); ++ } else { ++ ret = exfat_mount(sb, p_pbr); ++ } ++ ++ brelse(tmp_bh); ++ ++ if (ret) { ++ bdev_close(sb); ++ return ret; ++ } ++ ++ if (p_fs->vol_type == EXFAT) { ++ ret = load_alloc_bitmap(sb); ++ if (ret) { ++ bdev_close(sb); ++ return ret; ++ } ++ ret = load_upcase_table(sb); ++ if (ret) { ++ free_alloc_bitmap(sb); ++ bdev_close(sb); ++ return ret; ++ } ++ } ++ ++ if (p_fs->dev_ejected) { ++ if (p_fs->vol_type == EXFAT) { ++ free_upcase_table(sb); ++ free_alloc_bitmap(sb); ++ } ++ bdev_close(sb); ++ return FFS_MEDIAERR; ++ } ++ ++ printk("[EXFAT] mounted successfully\n"); ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsUmountVol(struct super_block *sb) ++{ ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ printk("[EXFAT] trying to unmount...\n"); ++ ++ fs_sync(sb, 0); ++ fs_set_vol_flags(sb, VOL_CLEAN); ++ ++ if (p_fs->vol_type == EXFAT) { ++ free_upcase_table(sb); ++ free_alloc_bitmap(sb); ++ } ++ ++ FAT_release_all(sb); ++ buf_release_all(sb); ++ ++ bdev_close(sb); ++ ++ if (p_fs->dev_ejected) { ++ printk( "[EXFAT] unmounted with media errors. " ++ "device's already ejected.\n"); ++ return FFS_MEDIAERR; ++ } ++ ++ printk("[EXFAT] unmounted successfully\n"); ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info) ++{ ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (p_fs->used_clusters == (UINT32) ~0) ++ p_fs->used_clusters = p_fs->fs_func->count_used_clusters(sb); ++ ++ info->FatType = p_fs->vol_type; ++ info->ClusterSize = p_fs->cluster_size; ++ info->NumClusters = p_fs->num_clusters - 2; ++ info->UsedClusters = p_fs->used_clusters; ++ info->FreeClusters = info->NumClusters - info->UsedClusters; ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsSyncVol(struct super_block *sb, INT32 do_sync) ++{ ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ fs_sync(sb, do_sync); ++ fs_set_vol_flags(sb, VOL_CLEAN); ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid) ++{ ++ INT32 ret, dentry, num_entries; ++ CHAIN_T dir; ++ UNI_NAME_T uni_name; ++ DOS_NAME_T dos_name; ++ DENTRY_T *ep, *ep2; ++ ENTRY_SET_CACHE_T *es=NULL; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ PRINTK("ffsLookupFile entered\n"); ++ ++ ret = resolve_path(inode, path, &dir, &uni_name); ++ if (ret) ++ return ret; ++ ++ ret = get_num_entries_and_dos_name(sb, &dir, &uni_name, &num_entries, &dos_name); ++ if (ret) ++ return ret; ++ ++ dentry = p_fs->fs_func->find_dir_entry(sb, &dir, &uni_name, num_entries, &dos_name, TYPE_ALL); ++ if (dentry < -1) ++ return FFS_NOTFOUND; ++ ++ fid->dir.dir = dir.dir; ++ fid->dir.size = dir.size; ++ fid->dir.flags = dir.flags; ++ fid->entry = dentry; ++ ++ if (dentry == -1) { ++ fid->type = TYPE_DIR; ++ fid->rwoffset = 0; ++ fid->hint_last_off = -1; ++ ++ fid->attr = ATTR_SUBDIR; ++ fid->flags = 0x01; ++ fid->size = 0; ++ fid->start_clu = p_fs->root_dir; ++ } else { ++ if (p_fs->vol_type == EXFAT) { ++ es = get_entry_set_in_dir(sb, &dir, dentry, ES_2_ENTRIES, &ep); ++ if (!es) ++ return FFS_MEDIAERR; ++ ep2 = ep+1; ++ } else { ++ ep = get_entry_in_dir(sb, &dir, dentry, NULL); ++ if (!ep) ++ return FFS_MEDIAERR; ++ ep2 = ep; ++ } ++ ++ fid->type = p_fs->fs_func->get_entry_type(ep); ++ fid->rwoffset = 0; ++ fid->hint_last_off = -1; ++ fid->attr = p_fs->fs_func->get_entry_attr(ep); ++ ++ fid->size = p_fs->fs_func->get_entry_size(ep2); ++ if ((fid->type == TYPE_FILE) && (fid->size == 0)) { ++ fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; ++ fid->start_clu = CLUSTER_32(~0); ++ } else { ++ fid->flags = p_fs->fs_func->get_entry_flag(ep2); ++ fid->start_clu = p_fs->fs_func->get_entry_clu0(ep2); ++ } ++ ++ if (p_fs->vol_type == EXFAT) ++ release_entry_set(es); ++ } ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ PRINTK("ffsLookupFile exited successfully\n"); ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid) ++{ ++ INT32 ret; ++ CHAIN_T dir; ++ UNI_NAME_T uni_name; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ ret = resolve_path(inode, path, &dir, &uni_name); ++ if (ret) ++ return ret; ++ ++ fs_set_vol_flags(sb, VOL_DIRTY); ++ ret = create_file(inode, &dir, &uni_name, mode, fid); ++ ++#if (DELAYED_SYNC == 0) ++ fs_sync(sb, 0); ++ fs_set_vol_flags(sb, VOL_CLEAN); ++#endif ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return ret; ++} ++ ++INT32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount) ++{ ++ INT32 offset, sec_offset, clu_offset; ++ UINT32 clu, LogSector; ++ UINT64 oneblkread, read_bytes; ++ struct buffer_head *tmp_bh = NULL; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ if (fid->type != TYPE_FILE) ++ return FFS_PERMISSIONERR; ++ ++ if (fid->rwoffset > fid->size) ++ fid->rwoffset = fid->size; ++ ++ if (count > (fid->size - fid->rwoffset)) ++ count = fid->size - fid->rwoffset; ++ ++ if (count == 0) { ++ if (rcount != NULL) ++ *rcount = 0; ++ return FFS_EOF; ++ } ++ ++ read_bytes = 0; ++ ++ while (count > 0) { ++ clu_offset = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); ++ clu = fid->start_clu; ++ ++ if (fid->flags == 0x03) { ++ clu += clu_offset; ++ } else { ++ if ((clu_offset > 0) && (fid->hint_last_off > 0) && ++ (clu_offset >= fid->hint_last_off)) { ++ clu_offset -= fid->hint_last_off; ++ clu = fid->hint_last_clu; ++ } ++ ++ while (clu_offset > 0) { ++ if (FAT_read(sb, clu, &clu) == -1) ++ return FFS_MEDIAERR; ++ ++ clu_offset--; ++ } ++ } ++ ++ fid->hint_last_off = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); ++ fid->hint_last_clu = clu; ++ ++ offset = (INT32)(fid->rwoffset & (p_fs->cluster_size-1)); ++ sec_offset = offset >> p_bd->sector_size_bits; ++ offset &= p_bd->sector_size_mask; ++ ++ LogSector = START_SECTOR(clu) + sec_offset; ++ ++ oneblkread = (UINT64)(p_bd->sector_size - offset); ++ if (oneblkread > count) ++ oneblkread = count; ++ ++ if ((offset == 0) && (oneblkread == p_bd->sector_size)) { ++ if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) ++ goto err_out; ++ MEMCPY(((INT8 *) buffer)+read_bytes, ((INT8 *) tmp_bh->b_data), (INT32) oneblkread); ++ } else { ++ if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) ++ goto err_out; ++ MEMCPY(((INT8 *) buffer)+read_bytes, ((INT8 *) tmp_bh->b_data)+offset, (INT32) oneblkread); ++ } ++ count -= oneblkread; ++ read_bytes += oneblkread; ++ fid->rwoffset += oneblkread; ++ } ++ brelse(tmp_bh); ++ ++err_out: ++ if (rcount != NULL) ++ *rcount = read_bytes; ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount) ++{ ++ INT32 modified = FALSE, offset, sec_offset, clu_offset; ++ INT32 num_clusters, num_alloc, num_alloced = (INT32) ~0; ++ UINT32 clu, last_clu, LogSector, sector; ++ UINT64 oneblkwrite, write_bytes; ++ CHAIN_T new_clu; ++ TIMESTAMP_T tm; ++ DENTRY_T *ep, *ep2; ++ ENTRY_SET_CACHE_T *es = NULL; ++ struct buffer_head *tmp_bh = NULL; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ if (fid->type != TYPE_FILE) ++ return FFS_PERMISSIONERR; ++ ++ if (fid->rwoffset > fid->size) ++ fid->rwoffset = fid->size; ++ ++ if (count == 0) { ++ if (wcount != NULL) ++ *wcount = 0; ++ return FFS_SUCCESS; ++ } ++ ++ fs_set_vol_flags(sb, VOL_DIRTY); ++ ++ if (fid->size == 0) ++ num_clusters = 0; ++ else ++ num_clusters = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; ++ ++ write_bytes = 0; ++ ++ while (count > 0) { ++ clu_offset = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); ++ clu = last_clu = fid->start_clu; ++ ++ if (fid->flags == 0x03) { ++ if ((clu_offset > 0) && (clu != CLUSTER_32(~0))) { ++ last_clu += clu_offset - 1; ++ ++ if (clu_offset == num_clusters) ++ clu = CLUSTER_32(~0); ++ else ++ clu += clu_offset; ++ } ++ } else { ++ if ((clu_offset > 0) && (fid->hint_last_off > 0) && ++ (clu_offset >= fid->hint_last_off)) { ++ clu_offset -= fid->hint_last_off; ++ clu = fid->hint_last_clu; ++ } ++ ++ while ((clu_offset > 0) && (clu != CLUSTER_32(~0))) { ++ last_clu = clu; ++ if (FAT_read(sb, clu, &clu) == -1) ++ return FFS_MEDIAERR; ++ ++ clu_offset--; ++ } ++ } ++ ++ if (clu == CLUSTER_32(~0)) { ++ num_alloc = (INT32)((count-1) >> p_fs->cluster_size_bits) + 1; ++ new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) : last_clu+1; ++ new_clu.size = 0; ++ new_clu.flags = fid->flags; ++ ++ num_alloced = p_fs->fs_func->alloc_cluster(sb, num_alloc, &new_clu); ++ if (num_alloced == 0) ++ break; ++ ++ if (last_clu == CLUSTER_32(~0)) { ++ if (new_clu.flags == 0x01) ++ fid->flags = 0x01; ++ fid->start_clu = new_clu.dir; ++ modified = TRUE; ++ } else { ++ if (new_clu.flags != fid->flags) { ++ exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters); ++ fid->flags = 0x01; ++ modified = TRUE; ++ } ++ if (new_clu.flags == 0x01) ++ FAT_write(sb, last_clu, new_clu.dir); ++ } ++ ++ num_clusters += num_alloced; ++ clu = new_clu.dir; ++ } ++ ++ fid->hint_last_off = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); ++ fid->hint_last_clu = clu; ++ ++ offset = (INT32)(fid->rwoffset & (p_fs->cluster_size-1)); ++ sec_offset = offset >> p_bd->sector_size_bits; ++ offset &= p_bd->sector_size_mask; ++ ++ LogSector = START_SECTOR(clu) + sec_offset; ++ ++ oneblkwrite = (UINT64)(p_bd->sector_size - offset); ++ if (oneblkwrite > count) ++ oneblkwrite = count; ++ ++ if ((offset == 0) && (oneblkwrite == p_bd->sector_size)) { ++ if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS) ++ goto err_out; ++ MEMCPY(((INT8 *) tmp_bh->b_data), ((INT8 *) buffer)+write_bytes, (INT32) oneblkwrite); ++ if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) { ++ brelse(tmp_bh); ++ goto err_out; ++ } ++ } else { ++ if ((offset > 0) || ((fid->rwoffset+oneblkwrite) < fid->size)) { ++ if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) ++ goto err_out; ++ } else { ++ if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS) ++ goto err_out; ++ } ++ ++ MEMCPY(((INT8 *) tmp_bh->b_data)+offset, ((INT8 *) buffer)+write_bytes, (INT32) oneblkwrite); ++ if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) { ++ brelse(tmp_bh); ++ goto err_out; ++ } ++ } ++ ++ count -= oneblkwrite; ++ write_bytes += oneblkwrite; ++ fid->rwoffset += oneblkwrite; ++ ++ fid->attr |= ATTR_ARCHIVE; ++ ++ if (fid->size < fid->rwoffset) { ++ fid->size = fid->rwoffset; ++ modified = TRUE; ++ } ++ } ++ ++ brelse(tmp_bh); ++ ++ if (p_fs->vol_type == EXFAT) { ++ es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); ++ if (es == NULL) ++ goto err_out; ++ ep2 = ep+1; ++ } else { ++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); ++ if (!ep) ++ goto err_out; ++ ep2 = ep; ++ } ++ ++ p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY); ++ p_fs->fs_func->set_entry_attr(ep, fid->attr); ++ ++ if (p_fs->vol_type != EXFAT) ++ buf_modify(sb, sector); ++ ++ if (modified) { ++ if (p_fs->fs_func->get_entry_flag(ep2) != fid->flags) ++ p_fs->fs_func->set_entry_flag(ep2, fid->flags); ++ ++ if (p_fs->fs_func->get_entry_size(ep2) != fid->size) ++ p_fs->fs_func->set_entry_size(ep2, fid->size); ++ ++ if (p_fs->fs_func->get_entry_clu0(ep2) != fid->start_clu) ++ p_fs->fs_func->set_entry_clu0(ep2, fid->start_clu); ++ ++ if (p_fs->vol_type != EXFAT) ++ buf_modify(sb, sector); ++ } ++ ++ if (p_fs->vol_type == EXFAT) { ++ update_dir_checksum_with_entry_set(sb, es); ++ release_entry_set(es); ++ } ++ ++#if (DELAYED_SYNC == 0) ++ fs_sync(sb, 0); ++ fs_set_vol_flags(sb, VOL_CLEAN); ++#endif ++ ++err_out: ++ if (wcount != NULL) ++ *wcount = write_bytes; ++ ++ if (num_alloced == 0) ++ return FFS_FULL; ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size) ++{ ++ INT32 num_clusters; ++ UINT32 last_clu = CLUSTER_32(0), sector; ++ CHAIN_T clu; ++ TIMESTAMP_T tm; ++ DENTRY_T *ep, *ep2; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); ++ ENTRY_SET_CACHE_T *es=NULL; ++ ++ if (fid->type != TYPE_FILE) ++ return FFS_PERMISSIONERR; ++ ++ if (fid->size != old_size) { ++ printk(KERN_ERR "[EXFAT] truncate : can't skip it because of " ++ "size-mismatch(old:%lld->fid:%lld).\n" ++ ,old_size, fid->size); ++ } ++ ++ if (old_size <= new_size) ++ return FFS_SUCCESS; ++ ++ fs_set_vol_flags(sb, VOL_DIRTY); ++ ++ clu.dir = fid->start_clu; ++ clu.size = (INT32)((old_size-1) >> p_fs->cluster_size_bits) + 1; ++ clu.flags = fid->flags; ++ ++ if (new_size > 0) { ++ num_clusters = (INT32)((new_size-1) >> p_fs->cluster_size_bits) + 1; ++ ++ if (clu.flags == 0x03) { ++ clu.dir += num_clusters; ++ } else { ++ while (num_clusters > 0) { ++ last_clu = clu.dir; ++ if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) ++ return FFS_MEDIAERR; ++ num_clusters--; ++ } ++ } ++ ++ clu.size -= num_clusters; ++ } ++ ++ fid->size = new_size; ++ fid->attr |= ATTR_ARCHIVE; ++ if (new_size == 0) { ++ fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; ++ fid->start_clu = CLUSTER_32(~0); ++ } ++ ++ if (p_fs->vol_type == EXFAT) { ++ es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); ++ if (es == NULL) ++ return FFS_MEDIAERR; ++ ep2 = ep+1; ++ } else { ++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); ++ if (!ep) ++ return FFS_MEDIAERR; ++ ep2 = ep; ++ } ++ ++ p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY); ++ p_fs->fs_func->set_entry_attr(ep, fid->attr); ++ ++ p_fs->fs_func->set_entry_size(ep2, new_size); ++ if (new_size == 0) { ++ p_fs->fs_func->set_entry_flag(ep2, 0x01); ++ p_fs->fs_func->set_entry_clu0(ep2, CLUSTER_32(0)); ++ } ++ ++ if (p_fs->vol_type != EXFAT) ++ buf_modify(sb, sector); ++ else { ++ update_dir_checksum_with_entry_set(sb, es); ++ release_entry_set(es); ++ } ++ ++ if (last_clu != CLUSTER_32(0)) { ++ if (fid->flags == 0x01) ++ FAT_write(sb, last_clu, CLUSTER_32(~0)); ++ } ++ ++ p_fs->fs_func->free_cluster(sb, &clu, 0); ++ ++ fid->hint_last_off = -1; ++ if (fid->rwoffset > fid->size) { ++ fid->rwoffset = fid->size; ++ } ++ ++#if (DELAYED_SYNC == 0) ++ fs_sync(sb, 0); ++ fs_set_vol_flags(sb, VOL_CLEAN); ++#endif ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return FFS_SUCCESS; ++} ++ ++static void update_parent_info( FILE_ID_T *fid, struct inode *parent_inode) ++{ ++ FS_INFO_T *p_fs = &(EXFAT_SB(parent_inode->i_sb)->fs_info); ++ FILE_ID_T *parent_fid = &(EXFAT_I(parent_inode)->fid); ++ ++ if (unlikely((parent_fid->flags != fid->dir.flags) ++ || (parent_fid->size != (fid->dir.size<<p_fs->cluster_size_bits)) ++ || (parent_fid->start_clu != fid->dir.dir))) { ++ ++ fid->dir.dir = parent_fid->start_clu; ++ fid->dir.flags = parent_fid->flags; ++ fid->dir.size = ((parent_fid->size + (p_fs->cluster_size-1)) ++ >> p_fs->cluster_size_bits); ++ } ++} ++ ++INT32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) ++{ ++ INT32 ret; ++ INT32 dentry; ++ CHAIN_T olddir, newdir; ++ CHAIN_T *p_dir=NULL; ++ UNI_NAME_T uni_name; ++ DENTRY_T *ep; ++ struct super_block *sb = old_parent_inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ UINT8 *new_path = (UINT8 *) new_dentry->d_name.name; ++ struct inode *new_inode = new_dentry->d_inode; ++ int num_entries; ++ FILE_ID_T *new_fid = NULL; ++ INT32 new_entry=0; ++ ++ if ((new_path == NULL) || (STRLEN(new_path) == 0)) ++ return FFS_ERROR; ++ ++ update_parent_info(fid, old_parent_inode); ++ ++ olddir.dir = fid->dir.dir; ++ olddir.size = fid->dir.size; ++ olddir.flags = fid->dir.flags; ++ ++ dentry = fid->entry; ++ ++ if (p_fs->vol_type != EXFAT) { ++ if ((olddir.dir != p_fs->root_dir) && (dentry < 2)) ++ return FFS_PERMISSIONERR; ++ } ++ ++ ep = get_entry_in_dir(sb, &olddir, dentry, NULL); ++ if (!ep) ++ return FFS_MEDIAERR; ++ ++ if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY) ++ return FFS_PERMISSIONERR; ++ ++ if (new_inode) { ++ UINT32 entry_type; ++ ++ ret = FFS_MEDIAERR; ++ new_fid = &EXFAT_I(new_inode)->fid; ++ ++ update_parent_info(new_fid, new_parent_inode); ++ ++ p_dir = &(new_fid->dir); ++ new_entry = new_fid->entry; ++ ep = get_entry_in_dir(sb, p_dir, new_entry, NULL); ++ if (!ep) ++ goto out; ++ ++ entry_type = p_fs->fs_func->get_entry_type(ep); ++ ++ if (entry_type == TYPE_DIR) { ++ CHAIN_T new_clu; ++ new_clu.dir = new_fid->start_clu; ++ new_clu.size = (INT32)((new_fid->size-1) >> p_fs->cluster_size_bits) + 1; ++ new_clu.flags = new_fid->flags; ++ ++ if (!is_dir_empty(sb, &new_clu)) ++ return FFS_FILEEXIST; ++ } ++ } ++ ++ ret = resolve_path(new_parent_inode, new_path, &newdir, &uni_name); ++ if (ret) ++ return ret; ++ ++ fs_set_vol_flags(sb, VOL_DIRTY); ++ ++ if (olddir.dir == newdir.dir) ++ ret = rename_file(new_parent_inode, &olddir, dentry, &uni_name, fid); ++ else ++ ret = move_file(new_parent_inode, &olddir, dentry, &newdir, &uni_name, fid); ++ ++ if ((ret == FFS_SUCCESS) && new_inode) { ++ ep = get_entry_in_dir(sb, p_dir, new_entry, NULL); ++ if (!ep) ++ goto out; ++ ++ num_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, new_entry, ep); ++ if (num_entries < 0) ++ goto out; ++ p_fs->fs_func->delete_dir_entry(sb, p_dir, new_entry, 0, num_entries+1); ++ } ++out: ++#if (DELAYED_SYNC == 0) ++ fs_sync(sb, 0); ++ fs_set_vol_flags(sb, VOL_CLEAN); ++#endif ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return ret; ++} ++ ++INT32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid) ++{ ++ INT32 dentry; ++ CHAIN_T dir, clu_to_free; ++ DENTRY_T *ep; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ dir.dir = fid->dir.dir; ++ dir.size = fid->dir.size; ++ dir.flags = fid->dir.flags; ++ ++ dentry = fid->entry; ++ ++ ep = get_entry_in_dir(sb, &dir, dentry, NULL); ++ if (!ep) ++ return FFS_MEDIAERR; ++ ++ if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY) ++ return FFS_PERMISSIONERR; ++ ++ fs_set_vol_flags(sb, VOL_DIRTY); ++ ++ remove_file(inode, &dir, dentry); ++ ++ clu_to_free.dir = fid->start_clu; ++ clu_to_free.size = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; ++ clu_to_free.flags = fid->flags; ++ ++ p_fs->fs_func->free_cluster(sb, &clu_to_free, 0); ++ ++ ++ fid->size = 0; ++ fid->start_clu = CLUSTER_32(~0); ++ fid->flags = (p_fs->vol_type == EXFAT)? 0x03: 0x01; ++ ++#if (DELAYED_SYNC == 0) ++ fs_sync(sb, 0); ++ fs_set_vol_flags(sb, VOL_CLEAN); ++#endif ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsSetAttr(struct inode *inode, UINT32 attr) ++{ ++ UINT32 type, sector; ++ DENTRY_T *ep; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); ++ UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; ++ ENTRY_SET_CACHE_T *es = NULL; ++ ++ if (fid->attr == attr) { ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ return FFS_SUCCESS; ++ } ++ ++ if (is_dir) { ++ if ((fid->dir.dir == p_fs->root_dir) && ++ (fid->entry == -1)) { ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ return FFS_SUCCESS; ++ } ++ } ++ ++ if (p_fs->vol_type == EXFAT) { ++ es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); ++ if (es == NULL) ++ return FFS_MEDIAERR; ++ } else { ++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); ++ if (!ep) ++ return FFS_MEDIAERR; ++ } ++ ++ type = p_fs->fs_func->get_entry_type(ep); ++ ++ if (((type == TYPE_FILE) && (attr & ATTR_SUBDIR)) || ++ ((type == TYPE_DIR) && (!(attr & ATTR_SUBDIR)))) { ++ INT32 err; ++ if (p_fs->dev_ejected) ++ err = FFS_MEDIAERR; ++ else ++ err = FFS_ERROR; ++ ++ if (p_fs->vol_type == EXFAT) ++ release_entry_set(es); ++ return err; ++ } ++ ++ fs_set_vol_flags(sb, VOL_DIRTY); ++ ++ fid->attr = attr; ++ p_fs->fs_func->set_entry_attr(ep, attr); ++ ++ if (p_fs->vol_type != EXFAT) ++ buf_modify(sb, sector); ++ else { ++ update_dir_checksum_with_entry_set(sb, es); ++ release_entry_set(es); ++ } ++ ++#if (DELAYED_SYNC == 0) ++ fs_sync(sb, 0); ++ fs_set_vol_flags(sb, VOL_CLEAN); ++#endif ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info) ++{ ++ UINT32 sector; ++ INT32 count; ++ CHAIN_T dir; ++ UNI_NAME_T uni_name; ++ TIMESTAMP_T tm; ++ DENTRY_T *ep, *ep2; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); ++ ENTRY_SET_CACHE_T *es=NULL; ++ UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; ++ ++ PRINTK("ffsGetStat entered\n"); ++ ++ if (is_dir) { ++ if ((fid->dir.dir == p_fs->root_dir) && ++ (fid->entry == -1)) { ++ info->Attr = ATTR_SUBDIR; ++ MEMSET((INT8 *) &info->CreateTimestamp, 0, sizeof(DATE_TIME_T)); ++ MEMSET((INT8 *) &info->ModifyTimestamp, 0, sizeof(DATE_TIME_T)); ++ MEMSET((INT8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); ++ STRCPY(info->ShortName, "."); ++ STRCPY(info->Name, "."); ++ ++ dir.dir = p_fs->root_dir; ++ dir.flags = 0x01; ++ ++ if (p_fs->root_dir == CLUSTER_32(0)) ++ info->Size = p_fs->dentries_in_root << DENTRY_SIZE_BITS; ++ else ++ info->Size = count_num_clusters(sb, &dir) << p_fs->cluster_size_bits; ++ ++ count = count_dos_name_entries(sb, &dir, TYPE_DIR); ++ if (count < 0) ++ return FFS_MEDIAERR; ++ info->NumSubdirs = count; ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ return FFS_SUCCESS; ++ } ++ } ++ ++ if (p_fs->vol_type == EXFAT) { ++ es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_2_ENTRIES, &ep); ++ if (es == NULL) ++ return FFS_MEDIAERR; ++ ep2 = ep+1; ++ } else { ++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); ++ if (!ep) ++ return FFS_MEDIAERR; ++ ep2 = ep; ++ buf_lock(sb, sector); ++ } ++ ++ info->Attr = p_fs->fs_func->get_entry_attr(ep); ++ ++ p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE); ++ info->CreateTimestamp.Year = tm.year; ++ info->CreateTimestamp.Month = tm.mon; ++ info->CreateTimestamp.Day = tm.day; ++ info->CreateTimestamp.Hour = tm.hour; ++ info->CreateTimestamp.Minute = tm.min; ++ info->CreateTimestamp.Second = tm.sec; ++ info->CreateTimestamp.MilliSecond = 0; ++ ++ p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY); ++ info->ModifyTimestamp.Year = tm.year; ++ info->ModifyTimestamp.Month = tm.mon; ++ info->ModifyTimestamp.Day = tm.day; ++ info->ModifyTimestamp.Hour = tm.hour; ++ info->ModifyTimestamp.Minute = tm.min; ++ info->ModifyTimestamp.Second = tm.sec; ++ info->ModifyTimestamp.MilliSecond = 0; ++ ++ MEMSET((INT8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); ++ ++ *(uni_name.name) = 0x0; ++ p_fs->fs_func->get_uni_name_from_ext_entry(sb, &(fid->dir), fid->entry, uni_name.name); ++ if (*(uni_name.name) == 0x0) ++ get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); ++ nls_uniname_to_cstring(sb, info->Name, &uni_name); ++ ++ if (p_fs->vol_type == EXFAT) { ++ info->NumSubdirs = 2; ++ } else { ++ buf_unlock(sb, sector); ++ get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0); ++ nls_uniname_to_cstring(sb, info->ShortName, &uni_name); ++ info->NumSubdirs = 0; ++ } ++ ++ info->Size = p_fs->fs_func->get_entry_size(ep2); ++ ++ if (p_fs->vol_type == EXFAT) ++ release_entry_set(es); ++ ++ if (is_dir) { ++ dir.dir = fid->start_clu; ++ dir.flags = 0x01; ++ ++ if (info->Size == 0) ++ info->Size = (UINT64) count_num_clusters(sb, &dir) << p_fs->cluster_size_bits; ++ ++ count = count_dos_name_entries(sb, &dir, TYPE_DIR); ++ if (count < 0) ++ return FFS_MEDIAERR; ++ info->NumSubdirs += count; ++ } ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ PRINTK("ffsGetStat exited successfully\n"); ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info) ++{ ++ UINT32 sector; ++ TIMESTAMP_T tm; ++ DENTRY_T *ep, *ep2; ++ ENTRY_SET_CACHE_T *es=NULL; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); ++ UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; ++ ++ if (is_dir) { ++ if ((fid->dir.dir == p_fs->root_dir) && ++ (fid->entry == -1)) { ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ return FFS_SUCCESS; ++ } ++ } ++ ++ fs_set_vol_flags(sb, VOL_DIRTY); ++ ++ if (p_fs->vol_type == EXFAT) { ++ es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); ++ if (es == NULL) ++ return FFS_MEDIAERR; ++ ep2 = ep+1; ++ } else { ++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); ++ if (!ep) ++ return FFS_MEDIAERR; ++ ep2 = ep; ++ } ++ ++ ++ p_fs->fs_func->set_entry_attr(ep, info->Attr); ++ ++ tm.sec = info->CreateTimestamp.Second; ++ tm.min = info->CreateTimestamp.Minute; ++ tm.hour = info->CreateTimestamp.Hour; ++ tm.day = info->CreateTimestamp.Day; ++ tm.mon = info->CreateTimestamp.Month; ++ tm.year = info->CreateTimestamp.Year; ++ p_fs->fs_func->set_entry_time(ep, &tm, TM_CREATE); ++ ++ tm.sec = info->ModifyTimestamp.Second; ++ tm.min = info->ModifyTimestamp.Minute; ++ tm.hour = info->ModifyTimestamp.Hour; ++ tm.day = info->ModifyTimestamp.Day; ++ tm.mon = info->ModifyTimestamp.Month; ++ tm.year = info->ModifyTimestamp.Year; ++ p_fs->fs_func->set_entry_time(ep, &tm, TM_MODIFY); ++ ++ ++ p_fs->fs_func->set_entry_size(ep2, info->Size); ++ ++ if (p_fs->vol_type != EXFAT) { ++ buf_modify(sb, sector); ++ } else { ++ update_dir_checksum_with_entry_set(sb, es); ++ release_entry_set(es); ++ } ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu) ++{ ++ INT32 num_clusters, num_alloced, modified = FALSE; ++ UINT32 last_clu, sector; ++ CHAIN_T new_clu; ++ DENTRY_T *ep; ++ ENTRY_SET_CACHE_T *es = NULL; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); ++ ++ fid->rwoffset = (INT64)(clu_offset) << p_fs->cluster_size_bits; ++ ++ if (EXFAT_I(inode)->mmu_private == 0) ++ num_clusters = 0; ++ else ++ num_clusters = (INT32)((EXFAT_I(inode)->mmu_private-1) >> p_fs->cluster_size_bits) + 1; ++ ++ *clu = last_clu = fid->start_clu; ++ ++ if (fid->flags == 0x03) { ++ if ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) { ++ last_clu += clu_offset - 1; ++ ++ if (clu_offset == num_clusters) ++ *clu = CLUSTER_32(~0); ++ else ++ *clu += clu_offset; ++ } ++ } else { ++ if ((clu_offset > 0) && (fid->hint_last_off > 0) && ++ (clu_offset >= fid->hint_last_off)) { ++ clu_offset -= fid->hint_last_off; ++ *clu = fid->hint_last_clu; ++ } ++ ++ while ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) { ++ last_clu = *clu; ++ if (FAT_read(sb, *clu, clu) == -1) ++ return FFS_MEDIAERR; ++ clu_offset--; ++ } ++ } ++ ++ if (*clu == CLUSTER_32(~0)) { ++ fs_set_vol_flags(sb, VOL_DIRTY); ++ ++ new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) : last_clu+1; ++ new_clu.size = 0; ++ new_clu.flags = fid->flags; ++ ++ num_alloced = p_fs->fs_func->alloc_cluster(sb, 1, &new_clu); ++ if (num_alloced < 1) ++ return FFS_FULL; ++ ++ if (last_clu == CLUSTER_32(~0)) { ++ if (new_clu.flags == 0x01) ++ fid->flags = 0x01; ++ fid->start_clu = new_clu.dir; ++ modified = TRUE; ++ } else { ++ if (new_clu.flags != fid->flags) { ++ exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters); ++ fid->flags = 0x01; ++ modified = TRUE; ++ } ++ if (new_clu.flags == 0x01) ++ FAT_write(sb, last_clu, new_clu.dir); ++ } ++ ++ num_clusters += num_alloced; ++ *clu = new_clu.dir; ++ ++ if (p_fs->vol_type == EXFAT) { ++ es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); ++ if (es == NULL) ++ return FFS_MEDIAERR; ++ ep++; ++ } ++ ++ if (modified) { ++ if (p_fs->vol_type != EXFAT) { ++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); ++ if (!ep) ++ return FFS_MEDIAERR; ++ } ++ ++ if (p_fs->fs_func->get_entry_flag(ep) != fid->flags) ++ p_fs->fs_func->set_entry_flag(ep, fid->flags); ++ ++ if (p_fs->fs_func->get_entry_clu0(ep) != fid->start_clu) ++ p_fs->fs_func->set_entry_clu0(ep, fid->start_clu); ++ ++ if (p_fs->vol_type != EXFAT) ++ buf_modify(sb, sector); ++ } ++ ++ if (p_fs->vol_type == EXFAT) { ++ update_dir_checksum_with_entry_set(sb, es); ++ release_entry_set(es); ++ } ++ ++ inode->i_blocks += num_alloced << (p_fs->cluster_size_bits - 9); ++ } ++ ++ fid->hint_last_off = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); ++ fid->hint_last_clu = *clu; ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid) ++{ ++ INT32 ret; ++ CHAIN_T dir; ++ UNI_NAME_T uni_name; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ PRINTK("ffsCreateDir entered\n"); ++ ++ ret = resolve_path(inode, path, &dir, &uni_name); ++ if (ret) ++ return ret; ++ ++ fs_set_vol_flags(sb, VOL_DIRTY); ++ ++ ret = create_dir(inode, &dir, &uni_name, fid); ++ ++#if (DELAYED_SYNC == 0) ++ fs_sync(sb, 0); ++ fs_set_vol_flags(sb, VOL_CLEAN); ++#endif ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return ret; ++} ++ ++INT32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) ++{ ++ INT32 i, dentry, clu_offset; ++ INT32 dentries_per_clu, dentries_per_clu_bits = 0; ++ UINT32 type, sector; ++ CHAIN_T dir, clu; ++ UNI_NAME_T uni_name; ++ TIMESTAMP_T tm; ++ DENTRY_T *ep; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); ++ ++ if (fid->type != TYPE_DIR) ++ return FFS_PERMISSIONERR; ++ ++ if (fid->entry == -1) { ++ dir.dir = p_fs->root_dir; ++ dir.flags = 0x01; ++ } else { ++ dir.dir = fid->start_clu; ++ dir.size = (INT32)(fid->size >> p_fs->cluster_size_bits); ++ dir.flags = fid->flags; ++ } ++ ++ dentry = (INT32) fid->rwoffset; ++ ++ if (dir.dir == CLUSTER_32(0)) { ++ dentries_per_clu = p_fs->dentries_in_root; ++ ++ if (dentry == dentries_per_clu) { ++ clu.dir = CLUSTER_32(~0); ++ } else { ++ clu.dir = dir.dir; ++ clu.size = dir.size; ++ clu.flags = dir.flags; ++ } ++ } else { ++ dentries_per_clu = p_fs->dentries_per_clu; ++ dentries_per_clu_bits = my_log2(dentries_per_clu); ++ ++ clu_offset = dentry >> dentries_per_clu_bits; ++ clu.dir = dir.dir; ++ clu.size = dir.size; ++ clu.flags = dir.flags; ++ ++ if (clu.flags == 0x03) { ++ clu.dir += clu_offset; ++ clu.size -= clu_offset; ++ } else { ++ if ((clu_offset > 0) && (fid->hint_last_off > 0) && ++ (clu_offset >= fid->hint_last_off)) { ++ clu_offset -= fid->hint_last_off; ++ clu.dir = fid->hint_last_clu; ++ } ++ ++ while (clu_offset > 0) { ++ if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) ++ return FFS_MEDIAERR; ++ ++ clu_offset--; ++ } ++ } ++ } ++ ++ while (clu.dir != CLUSTER_32(~0)) { ++ if (p_fs->dev_ejected) ++ break; ++ ++ if (dir.dir == CLUSTER_32(0)) ++ i = dentry % dentries_per_clu; ++ else ++ i = dentry & (dentries_per_clu-1); ++ ++ for ( ; i < dentries_per_clu; i++, dentry++) { ++ ep = get_entry_in_dir(sb, &clu, i, §or); ++ if (!ep) ++ return FFS_MEDIAERR; ++ ++ type = p_fs->fs_func->get_entry_type(ep); ++ ++ if (type == TYPE_UNUSED) ++ break; ++ ++ if ((type != TYPE_FILE) && (type != TYPE_DIR)) ++ continue; ++ ++ buf_lock(sb, sector); ++ dir_entry->Attr = p_fs->fs_func->get_entry_attr(ep); ++ ++ p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE); ++ dir_entry->CreateTimestamp.Year = tm.year; ++ dir_entry->CreateTimestamp.Month = tm.mon; ++ dir_entry->CreateTimestamp.Day = tm.day; ++ dir_entry->CreateTimestamp.Hour = tm.hour; ++ dir_entry->CreateTimestamp.Minute = tm.min; ++ dir_entry->CreateTimestamp.Second = tm.sec; ++ dir_entry->CreateTimestamp.MilliSecond = 0; ++ ++ p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY); ++ dir_entry->ModifyTimestamp.Year = tm.year; ++ dir_entry->ModifyTimestamp.Month = tm.mon; ++ dir_entry->ModifyTimestamp.Day = tm.day; ++ dir_entry->ModifyTimestamp.Hour = tm.hour; ++ dir_entry->ModifyTimestamp.Minute = tm.min; ++ dir_entry->ModifyTimestamp.Second = tm.sec; ++ dir_entry->ModifyTimestamp.MilliSecond = 0; ++ ++ MEMSET((INT8 *) &dir_entry->AccessTimestamp, 0, sizeof(DATE_TIME_T)); ++ ++ *(uni_name.name) = 0x0; ++ p_fs->fs_func->get_uni_name_from_ext_entry(sb, &dir, dentry, uni_name.name); ++ if (*(uni_name.name) == 0x0) ++ get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); ++ nls_uniname_to_cstring(sb, dir_entry->Name, &uni_name); ++ buf_unlock(sb, sector); ++ ++ if (p_fs->vol_type == EXFAT) { ++ ep = get_entry_in_dir(sb, &clu, i+1, NULL); ++ if (!ep) ++ return FFS_MEDIAERR; ++ } else { ++ get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0); ++ nls_uniname_to_cstring(sb, dir_entry->ShortName, &uni_name); ++ } ++ ++ dir_entry->Size = p_fs->fs_func->get_entry_size(ep); ++ ++ if (dir.dir == CLUSTER_32(0)) { ++ } else { ++ fid->hint_last_off = dentry >> dentries_per_clu_bits; ++ fid->hint_last_clu = clu.dir; ++ } ++ ++ fid->rwoffset = (INT64) ++dentry; ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return FFS_SUCCESS; ++ } ++ ++ if (dir.dir == CLUSTER_32(0)) ++ break; ++ ++ if (clu.flags == 0x03) { ++ if ((--clu.size) > 0) ++ clu.dir++; ++ else ++ clu.dir = CLUSTER_32(~0); ++ } else { ++ if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) ++ return FFS_MEDIAERR; ++ } ++ } ++ ++ *(dir_entry->Name) = '\0'; ++ ++ fid->rwoffset = (INT64) ++dentry; ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid) ++{ ++ INT32 dentry; ++ CHAIN_T dir, clu_to_free; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ dir.dir = fid->dir.dir; ++ dir.size = fid->dir.size; ++ dir.flags = fid->dir.flags; ++ ++ dentry = fid->entry; ++ ++ if (p_fs->vol_type != EXFAT) { ++ if ((dir.dir != p_fs->root_dir) && (dentry < 2)) ++ return FFS_PERMISSIONERR; ++ } ++ ++ clu_to_free.dir = fid->start_clu; ++ clu_to_free.size = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; ++ clu_to_free.flags = fid->flags; ++ ++ if (!is_dir_empty(sb, &clu_to_free)) ++ return FFS_FILEEXIST; ++ ++ fs_set_vol_flags(sb, VOL_DIRTY); ++ ++ remove_file(inode, &dir, dentry); ++ ++ p_fs->fs_func->free_cluster(sb, &clu_to_free, 1); ++ ++ fid->size = 0; ++ fid->start_clu = CLUSTER_32(~0); ++ fid->flags = (p_fs->vol_type == EXFAT)? 0x03: 0x01; ++ ++#if (DELAYED_SYNC == 0) ++ fs_sync(sb, 0); ++ fs_set_vol_flags(sb, VOL_CLEAN); ++#endif ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 fs_init(void) ++{ ++ if (sizeof(DENTRY_T) != DENTRY_SIZE) { ++ return FFS_ALIGNMENTERR; ++ } ++ ++ if (sizeof(DOS_DENTRY_T) != DENTRY_SIZE) { ++ return FFS_ALIGNMENTERR; ++ } ++ ++ if (sizeof(EXT_DENTRY_T) != DENTRY_SIZE) { ++ return FFS_ALIGNMENTERR; ++ } ++ ++ if (sizeof(FILE_DENTRY_T) != DENTRY_SIZE) { ++ return FFS_ALIGNMENTERR; ++ } ++ ++ if (sizeof(STRM_DENTRY_T) != DENTRY_SIZE) { ++ return FFS_ALIGNMENTERR; ++ } ++ ++ if (sizeof(NAME_DENTRY_T) != DENTRY_SIZE) { ++ return FFS_ALIGNMENTERR; ++ } ++ ++ if (sizeof(BMAP_DENTRY_T) != DENTRY_SIZE) { ++ return FFS_ALIGNMENTERR; ++ } ++ ++ if (sizeof(CASE_DENTRY_T) != DENTRY_SIZE) { ++ return FFS_ALIGNMENTERR; ++ } ++ ++ if (sizeof(VOLM_DENTRY_T) != DENTRY_SIZE) { ++ return FFS_ALIGNMENTERR; ++ } ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 fs_shutdown(void) ++{ ++ return FFS_SUCCESS; ++} ++ ++void fs_set_vol_flags(struct super_block *sb, UINT32 new_flag) ++{ ++ PBR_SECTOR_T *p_pbr; ++ BPBEX_T *p_bpb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (p_fs->vol_flag == new_flag) ++ return; ++ ++ p_fs->vol_flag = new_flag; ++ ++ if (p_fs->vol_type == EXFAT) { ++ if (p_fs->pbr_bh == NULL) { ++ if (sector_read(sb, p_fs->PBR_sector, &(p_fs->pbr_bh), 1) != FFS_SUCCESS) ++ return; ++ } ++ ++ p_pbr = (PBR_SECTOR_T *) p_fs->pbr_bh->b_data; ++ p_bpb = (BPBEX_T *) p_pbr->bpb; ++ SET16(p_bpb->vol_flags, (UINT16) new_flag); ++ ++ if ((new_flag == VOL_DIRTY) && (!buffer_dirty(p_fs->pbr_bh))) ++ sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 1); ++ else ++ sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 0); ++ } ++} ++ ++void fs_sync(struct super_block *sb, INT32 do_sync) ++{ ++ if (do_sync) ++ bdev_sync(sb); ++} ++ ++void fs_error(struct super_block *sb) ++{ ++ struct exfat_mount_options *opts = &EXFAT_SB(sb)->options; ++ ++ if (opts->errors == EXFAT_ERRORS_PANIC) ++ panic("[EXFAT] Filesystem panic from previous error\n"); ++ else if ((opts->errors == EXFAT_ERRORS_RO) && !(sb->s_flags & MS_RDONLY)) { ++ sb->s_flags |= MS_RDONLY; ++ printk(KERN_ERR "[EXFAT] Filesystem has been set read-only\n"); ++ } ++} ++ ++INT32 clear_cluster(struct super_block *sb, UINT32 clu) ++{ ++ UINT32 s, n; ++ INT32 ret = FFS_SUCCESS; ++ struct buffer_head *tmp_bh = NULL; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ if (clu == CLUSTER_32(0)) { ++ s = p_fs->root_start_sector; ++ n = p_fs->data_start_sector; ++ } else { ++ s = START_SECTOR(clu); ++ n = s + p_fs->sectors_per_clu; ++ } ++ ++ for ( ; s < n; s++) { ++ if ((ret = sector_read(sb, s, &tmp_bh, 0)) != FFS_SUCCESS) ++ return ret; ++ ++ MEMSET((INT8 *) tmp_bh->b_data, 0x0, p_bd->sector_size); ++ if ((ret = sector_write(sb, s, tmp_bh, 0)) !=FFS_SUCCESS) ++ break; ++ } ++ ++ brelse(tmp_bh); ++ return ret; ++} ++ ++INT32 fat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain) ++{ ++ INT32 i, num_clusters = 0; ++ UINT32 new_clu, last_clu = CLUSTER_32(~0), read_clu; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ new_clu = p_chain->dir; ++ if (new_clu == CLUSTER_32(~0)) ++ new_clu = p_fs->clu_srch_ptr; ++ else if (new_clu >= p_fs->num_clusters) ++ new_clu = 2; ++ ++ __set_sb_dirty(sb); ++ ++ p_chain->dir = CLUSTER_32(~0); ++ ++ for (i = 2; i < p_fs->num_clusters; i++) { ++ if (FAT_read(sb, new_clu, &read_clu) != 0) ++ return 0; ++ ++ if (read_clu == CLUSTER_32(0)) { ++ FAT_write(sb, new_clu, CLUSTER_32(~0)); ++ num_clusters++; ++ ++ if (p_chain->dir == CLUSTER_32(~0)) ++ p_chain->dir = new_clu; ++ else ++ FAT_write(sb, last_clu, new_clu); ++ ++ last_clu = new_clu; ++ ++ if ((--num_alloc) == 0) { ++ p_fs->clu_srch_ptr = new_clu; ++ if (p_fs->used_clusters != (UINT32) ~0) ++ p_fs->used_clusters += num_clusters; ++ ++ return(num_clusters); ++ } ++ } ++ if ((++new_clu) >= p_fs->num_clusters) ++ new_clu = 2; ++ } ++ ++ p_fs->clu_srch_ptr = new_clu; ++ if (p_fs->used_clusters != (UINT32) ~0) ++ p_fs->used_clusters += num_clusters; ++ ++ return(num_clusters); ++} ++ ++INT32 exfat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain) ++{ ++ INT32 num_clusters = 0; ++ UINT32 hint_clu, new_clu, last_clu = CLUSTER_32(~0); ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ hint_clu = p_chain->dir; ++ if (hint_clu == CLUSTER_32(~0)) { ++ hint_clu = test_alloc_bitmap(sb, p_fs->clu_srch_ptr-2); ++ if (hint_clu == CLUSTER_32(~0)) ++ return 0; ++ } else if (hint_clu >= p_fs->num_clusters) { ++ hint_clu = 2; ++ p_chain->flags = 0x01; ++ } ++ ++ __set_sb_dirty(sb); ++ ++ p_chain->dir = CLUSTER_32(~0); ++ ++ while ((new_clu = test_alloc_bitmap(sb, hint_clu-2)) != CLUSTER_32(~0)) { ++ if (new_clu != hint_clu) { ++ if (p_chain->flags == 0x03) { ++ exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters); ++ p_chain->flags = 0x01; ++ } ++ } ++ ++ if (set_alloc_bitmap(sb, new_clu-2) != FFS_SUCCESS) ++ return 0; ++ ++ num_clusters++; ++ ++ if (p_chain->flags == 0x01) ++ FAT_write(sb, new_clu, CLUSTER_32(~0)); ++ ++ if (p_chain->dir == CLUSTER_32(~0)) { ++ p_chain->dir = new_clu; ++ } else { ++ if (p_chain->flags == 0x01) ++ FAT_write(sb, last_clu, new_clu); ++ } ++ last_clu = new_clu; ++ ++ if ((--num_alloc) == 0) { ++ p_fs->clu_srch_ptr = hint_clu; ++ if (p_fs->used_clusters != (UINT32) ~0) ++ p_fs->used_clusters += num_clusters; ++ ++ p_chain->size += num_clusters; ++ return(num_clusters); ++ } ++ ++ hint_clu = new_clu + 1; ++ if (hint_clu >= p_fs->num_clusters) { ++ hint_clu = 2; ++ ++ if (p_chain->flags == 0x03) { ++ exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters); ++ p_chain->flags = 0x01; ++ } ++ } ++ } ++ ++ p_fs->clu_srch_ptr = hint_clu; ++ if (p_fs->used_clusters != (UINT32) ~0) ++ p_fs->used_clusters += num_clusters; ++ ++ p_chain->size += num_clusters; ++ return(num_clusters); ++} ++ ++void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse) ++{ ++ INT32 num_clusters = 0; ++ UINT32 clu, prev; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ INT32 i; ++ UINT32 sector; ++ ++ if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) ++ return; ++ __set_sb_dirty(sb); ++ clu = p_chain->dir; ++ ++ if (p_chain->size <= 0) ++ return; ++ ++ do { ++ if (p_fs->dev_ejected) ++ break; ++ ++ if (do_relse) { ++ sector = START_SECTOR(clu); ++ for (i = 0; i < p_fs->sectors_per_clu; i++) { ++ buf_release(sb, sector+i); ++ } ++ } ++ ++ prev = clu; ++ if (FAT_read(sb, clu, &clu) == -1) ++ break; ++ ++ FAT_write(sb, prev, CLUSTER_32(0)); ++ num_clusters++; ++ ++ } while (clu != CLUSTER_32(~0)); ++ ++ if (p_fs->used_clusters != (UINT32) ~0) ++ p_fs->used_clusters -= num_clusters; ++} ++ ++void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse) ++{ ++ INT32 num_clusters = 0; ++ UINT32 clu; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ INT32 i; ++ UINT32 sector; ++ ++ if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) ++ return; ++ ++ if (p_chain->size <= 0) { ++ printk(KERN_ERR "[EXFAT] free_cluster : skip free-req clu:%u, " ++ "because of zero-size truncation\n" ++ ,p_chain->dir); ++ return; ++ } ++ ++ __set_sb_dirty(sb); ++ clu = p_chain->dir; ++ ++ if (p_chain->flags == 0x03) { ++ do { ++ if (do_relse) { ++ sector = START_SECTOR(clu); ++ for (i = 0; i < p_fs->sectors_per_clu; i++) { ++ buf_release(sb, sector+i); ++ } ++ } ++ ++ if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS) ++ break; ++ clu++; ++ ++ num_clusters++; ++ } while (num_clusters < p_chain->size); ++ } else { ++ do { ++ if (p_fs->dev_ejected) ++ break; ++ ++ if (do_relse) { ++ sector = START_SECTOR(clu); ++ for (i = 0; i < p_fs->sectors_per_clu; i++) { ++ buf_release(sb, sector+i); ++ } ++ } ++ ++ if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS) ++ break; ++ ++ if (FAT_read(sb, clu, &clu) == -1) ++ break; ++ num_clusters++; ++ } while ((clu != CLUSTER_32(0)) && (clu != CLUSTER_32(~0))); ++ } ++ ++ if (p_fs->used_clusters != (UINT32) ~0) ++ p_fs->used_clusters -= num_clusters; ++} ++ ++UINT32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain) ++{ ++ UINT32 clu, next; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ clu = p_chain->dir; ++ ++ if (p_chain->flags == 0x03) { ++ clu += p_chain->size - 1; ++ } else { ++ while((FAT_read(sb, clu, &next) == 0) && (next != CLUSTER_32(~0))) { ++ if (p_fs->dev_ejected) ++ break; ++ clu = next; ++ } ++ } ++ ++ return(clu); ++} ++ ++INT32 count_num_clusters(struct super_block *sb, CHAIN_T *p_chain) ++{ ++ INT32 i, count = 0; ++ UINT32 clu; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) ++ return 0; ++ ++ clu = p_chain->dir; ++ ++ if (p_chain->flags == 0x03) { ++ count = p_chain->size; ++ } else { ++ for (i = 2; i < p_fs->num_clusters; i++) { ++ count++; ++ if (FAT_read(sb, clu, &clu) != 0) ++ return 0; ++ if (clu == CLUSTER_32(~0)) ++ break; ++ } ++ } ++ ++ return(count); ++} ++ ++INT32 fat_count_used_clusters(struct super_block *sb) ++{ ++ INT32 i, count = 0; ++ UINT32 clu; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ for (i = 2; i < p_fs->num_clusters; i++) { ++ if (FAT_read(sb, i, &clu) != 0) ++ break; ++ if (clu != CLUSTER_32(0)) ++ count++; ++ } ++ ++ return(count); ++} ++ ++INT32 exfat_count_used_clusters(struct super_block *sb) ++{ ++ INT32 i, map_i, map_b, count = 0; ++ UINT8 k; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ map_i = map_b = 0; ++ ++ for (i = 2; i < p_fs->num_clusters; i += 8) { ++ k = *(((UINT8 *) p_fs->vol_amap[map_i]->b_data) + map_b); ++ count += used_bit[k]; ++ ++ if ((++map_b) >= p_bd->sector_size) { ++ map_i++; ++ map_b = 0; ++ } ++ } ++ ++ return(count); ++} ++ ++void exfat_chain_cont_cluster(struct super_block *sb, UINT32 chain, INT32 len) ++{ ++ if (len == 0) ++ return; ++ ++ while (len > 1) { ++ FAT_write(sb, chain, chain+1); ++ chain++; ++ len--; ++ } ++ FAT_write(sb, chain, CLUSTER_32(~0)); ++} ++ ++INT32 load_alloc_bitmap(struct super_block *sb) ++{ ++ INT32 i, j, ret; ++ UINT32 map_size; ++ UINT32 type, sector; ++ CHAIN_T clu; ++ BMAP_DENTRY_T *ep; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ clu.dir = p_fs->root_dir; ++ clu.flags = 0x01; ++ ++ while (clu.dir != CLUSTER_32(~0)) { ++ if (p_fs->dev_ejected) ++ break; ++ ++ for (i = 0; i < p_fs->dentries_per_clu; i++) { ++ ep = (BMAP_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); ++ if (!ep) ++ return FFS_MEDIAERR; ++ ++ type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); ++ ++ if (type == TYPE_UNUSED) ++ break; ++ if (type != TYPE_BITMAP) ++ continue; ++ ++ if (ep->flags == 0x0) { ++ p_fs->map_clu = GET32_A(ep->start_clu); ++ map_size = (UINT32) GET64_A(ep->size); ++ ++ p_fs->map_sectors = ((map_size-1) >> p_bd->sector_size_bits) + 1; ++ ++ p_fs->vol_amap = (struct buffer_head **) MALLOC(sizeof(struct buffer_head *) * p_fs->map_sectors); ++ if (p_fs->vol_amap == NULL) ++ return FFS_MEMORYERR; ++ ++ sector = START_SECTOR(p_fs->map_clu); ++ ++ for (j = 0; j < p_fs->map_sectors; j++) { ++ p_fs->vol_amap[j] = NULL; ++ ret = sector_read(sb, sector+j, &(p_fs->vol_amap[j]), 1); ++ if (ret != FFS_SUCCESS) { ++ i=0; ++ while (i < j) ++ brelse(p_fs->vol_amap[i++]); ++ ++ FREE(p_fs->vol_amap); ++ p_fs->vol_amap = NULL; ++ return ret; ++ } ++ } ++ ++ p_fs->pbr_bh = NULL; ++ return FFS_SUCCESS; ++ } ++ } ++ ++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) ++ return FFS_MEDIAERR; ++ } ++ ++ return FFS_FORMATERR; ++} ++ ++void free_alloc_bitmap(struct super_block *sb) ++{ ++ INT32 i; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ brelse(p_fs->pbr_bh); ++ ++ for (i = 0; i < p_fs->map_sectors; i++) { ++ __brelse(p_fs->vol_amap[i]); ++ } ++ ++ FREE(p_fs->vol_amap); ++ p_fs->vol_amap = NULL; ++} ++ ++INT32 set_alloc_bitmap(struct super_block *sb, UINT32 clu) ++{ ++ INT32 i, b; ++ UINT32 sector; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ i = clu >> (p_bd->sector_size_bits + 3); ++ b = clu & ((p_bd->sector_size << 3) - 1); ++ ++ sector = START_SECTOR(p_fs->map_clu) + i; ++ ++ Bitmap_set((UINT8 *) p_fs->vol_amap[i]->b_data, b); ++ ++ return (sector_write(sb, sector, p_fs->vol_amap[i], 0)); ++} ++ ++INT32 clr_alloc_bitmap(struct super_block *sb, UINT32 clu) ++{ ++ INT32 i, b; ++ UINT32 sector; ++#if EXFAT_CONFIG_DISCARD ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ struct exfat_mount_options *opts = &sbi->options; ++ int ret; ++#endif ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ i = clu >> (p_bd->sector_size_bits + 3); ++ b = clu & ((p_bd->sector_size << 3) - 1); ++ ++ sector = START_SECTOR(p_fs->map_clu) + i; ++ ++ Bitmap_clear((UINT8 *) p_fs->vol_amap[i]->b_data, b); ++ ++ return (sector_write(sb, sector, p_fs->vol_amap[i], 0)); ++ ++#if EXFAT_CONFIG_DISCARD ++ if (opts->discard) { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++ ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits)); ++#else ++ ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits), GFP_NOFS, 0); ++#endif ++ if (ret == -EOPNOTSUPP) { ++ printk(KERN_WARNING "discard not supported by device, disabling"); ++ opts->discard = 0; ++ } ++ } ++#endif ++} ++ ++UINT32 test_alloc_bitmap(struct super_block *sb, UINT32 clu) ++{ ++ INT32 i, map_i, map_b; ++ UINT32 clu_base, clu_free; ++ UINT8 k, clu_mask; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ clu_base = (clu & ~(0x7)) + 2; ++ clu_mask = (1 << (clu - clu_base + 2)) - 1; ++ ++ map_i = clu >> (p_bd->sector_size_bits + 3); ++ map_b = (clu >> 3) & p_bd->sector_size_mask; ++ ++ for (i = 2; i < p_fs->num_clusters; i += 8) { ++ k = *(((UINT8 *) p_fs->vol_amap[map_i]->b_data) + map_b); ++ if (clu_mask > 0) { ++ k |= clu_mask; ++ clu_mask = 0; ++ } ++ if (k < 0xFF) { ++ clu_free = clu_base + free_bit[k]; ++ if (clu_free < p_fs->num_clusters) ++ return(clu_free); ++ } ++ clu_base += 8; ++ ++ if (((++map_b) >= p_bd->sector_size) || (clu_base >= p_fs->num_clusters)) { ++ if ((++map_i) >= p_fs->map_sectors) { ++ clu_base = 2; ++ map_i = 0; ++ } ++ map_b = 0; ++ } ++ } ++ ++ return(CLUSTER_32(~0)); ++} ++ ++void sync_alloc_bitmap(struct super_block *sb) ++{ ++ INT32 i; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (p_fs->vol_amap == NULL) ++ return; ++ ++ for (i = 0; i < p_fs->map_sectors; i++) { ++ sync_dirty_buffer(p_fs->vol_amap[i]); ++ } ++} ++ ++INT32 __load_upcase_table(struct super_block *sb, UINT32 sector, UINT32 num_sectors, UINT32 utbl_checksum) ++{ ++ INT32 i, ret = FFS_ERROR; ++ UINT32 j; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ struct buffer_head *tmp_bh = NULL; ++ ++ UINT8 skip = FALSE; ++ UINT32 index = 0; ++ UINT16 uni = 0; ++ UINT16 **upcase_table; ++ ++ UINT32 checksum = 0; ++ ++ upcase_table = p_fs->vol_utbl = (UINT16 **) MALLOC(UTBL_COL_COUNT * sizeof(UINT16 *)); ++ if(upcase_table == NULL) ++ return FFS_MEMORYERR; ++ MEMSET(upcase_table, 0, UTBL_COL_COUNT * sizeof(UINT16 *)); ++ ++ num_sectors += sector; ++ ++ while(sector < num_sectors) { ++ ret = sector_read(sb, sector, &tmp_bh, 1); ++ if (ret != FFS_SUCCESS) { ++ PRINTK("sector read (0x%X)fail\n", sector); ++ goto error; ++ } ++ sector++; ++ ++ for(i = 0; i < p_bd->sector_size && index <= 0xFFFF; i += 2) { ++ uni = GET16(((UINT8 *) tmp_bh->b_data)+i); ++ ++ checksum = ((checksum & 1) ? 0x80000000 : 0 ) + (checksum >> 1) + *(((UINT8 *) tmp_bh->b_data)+i); ++ checksum = ((checksum & 1) ? 0x80000000 : 0 ) + (checksum >> 1) + *(((UINT8 *) tmp_bh->b_data)+(i+1)); ++ ++ if(skip) { ++ PRINTK("skip from 0x%X ", index); ++ index += uni; ++ PRINTK("to 0x%X (amount of 0x%X)\n", index, uni); ++ skip = FALSE; ++ } else if(uni == index) ++ index++; ++ else if(uni == 0xFFFF) ++ skip = TRUE; ++ else { ++ UINT16 col_index = get_col_index(index); ++ ++ if(upcase_table[col_index]== NULL) { ++ PRINTK("alloc = 0x%X\n", col_index); ++ upcase_table[col_index] = (UINT16 *) MALLOC(UTBL_ROW_COUNT * sizeof(UINT16)); ++ if(upcase_table[col_index] == NULL) { ++ ret = FFS_MEMORYERR; ++ goto error; ++ } ++ ++ for(j = 0 ; j < UTBL_ROW_COUNT ; j++) ++ upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; ++ } ++ ++ upcase_table[col_index][get_row_index(index)] = uni; ++ index++; ++ } ++ } ++ } ++ if(index >= 0xFFFF && utbl_checksum == checksum) { ++ if(tmp_bh) ++ brelse(tmp_bh); ++ return FFS_SUCCESS; ++ } ++ ret = FFS_ERROR; ++error: ++ if(tmp_bh) ++ brelse(tmp_bh); ++ free_upcase_table(sb); ++ return ret; ++} ++ ++INT32 __load_default_upcase_table(struct super_block *sb) ++{ ++ INT32 i, ret = FFS_ERROR; ++ UINT32 j; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ UINT8 skip = FALSE; ++ UINT32 index = 0; ++ UINT16 uni = 0; ++ UINT16 **upcase_table; ++ ++ upcase_table = p_fs->vol_utbl = (UINT16 **) MALLOC(UTBL_COL_COUNT * sizeof(UINT16 *)); ++ if(upcase_table == NULL) ++ return FFS_MEMORYERR; ++ MEMSET(upcase_table, 0, UTBL_COL_COUNT * sizeof(UINT16 *)); ++ ++ for(i = 0; index <= 0xFFFF && i < NUM_UPCASE*2; i += 2) { ++ uni = GET16(uni_upcase + i); ++ if(skip) { ++ PRINTK("skip from 0x%X ", index); ++ index += uni; ++ PRINTK("to 0x%X (amount of 0x%X)\n", index, uni); ++ skip = FALSE; ++ } else if(uni == index) ++ index++; ++ else if(uni == 0xFFFF) ++ skip = TRUE; ++ else { ++ UINT16 col_index = get_col_index(index); ++ ++ if(upcase_table[col_index]== NULL) { ++ PRINTK("alloc = 0x%X\n", col_index); ++ upcase_table[col_index] = (UINT16 *) MALLOC(UTBL_ROW_COUNT * sizeof(UINT16)); ++ if(upcase_table[col_index] == NULL) { ++ ret = FFS_MEMORYERR; ++ goto error; ++ } ++ ++ for(j = 0 ; j < UTBL_ROW_COUNT ; j++) ++ upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; ++ } ++ ++ upcase_table[col_index][get_row_index(index)] = uni; ++ index ++; ++ } ++ } ++ ++ if(index >= 0xFFFF) ++ return FFS_SUCCESS; ++ ++error: ++ free_upcase_table(sb); ++ return ret; ++} ++ ++INT32 load_upcase_table(struct super_block *sb) ++{ ++ INT32 i; ++ UINT32 tbl_clu, tbl_size; ++ UINT32 type, sector, num_sectors; ++ CHAIN_T clu; ++ CASE_DENTRY_T *ep; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ clu.dir = p_fs->root_dir; ++ clu.flags = 0x01; ++ ++ if (p_fs->dev_ejected) ++ return FFS_MEDIAERR; ++ ++ while (clu.dir != CLUSTER_32(~0)) { ++ for (i = 0; i < p_fs->dentries_per_clu; i++) { ++ ep = (CASE_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); ++ if (!ep) ++ return FFS_MEDIAERR; ++ ++ type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); ++ ++ if (type == TYPE_UNUSED) ++ break; ++ if (type != TYPE_UPCASE) ++ continue; ++ ++ tbl_clu = GET32_A(ep->start_clu); ++ tbl_size = (UINT32) GET64_A(ep->size); ++ ++ sector = START_SECTOR(tbl_clu); ++ num_sectors = ((tbl_size-1) >> p_bd->sector_size_bits) + 1; ++ if(__load_upcase_table(sb, sector, num_sectors, GET32_A(ep->checksum)) != FFS_SUCCESS) ++ break; ++ else ++ return FFS_SUCCESS; ++ } ++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) ++ return FFS_MEDIAERR; ++ } ++ return __load_default_upcase_table(sb); ++} ++ ++void free_upcase_table(struct super_block *sb) ++{ ++ UINT32 i; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ UINT16 **upcase_table; ++ ++ upcase_table = p_fs->vol_utbl; ++ for(i = 0 ; i < UTBL_COL_COUNT ; i ++) ++ FREE(upcase_table[i]); ++ ++ FREE(p_fs->vol_utbl); ++ ++ p_fs->vol_utbl = NULL; ++} ++ ++UINT32 fat_get_entry_type(DENTRY_T *p_entry) ++{ ++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; ++ ++ if (*(ep->name) == 0x0) ++ return TYPE_UNUSED; ++ ++ else if (*(ep->name) == 0xE5) ++ return TYPE_DELETED; ++ ++ else if (ep->attr == ATTR_EXTEND) ++ return TYPE_EXTEND; ++ ++ else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_VOLUME) ++ return TYPE_VOLUME; ++ ++ else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_SUBDIR) ++ return TYPE_DIR; ++ ++ return TYPE_FILE; ++} ++ ++UINT32 exfat_get_entry_type(DENTRY_T *p_entry) ++{ ++ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; ++ ++ if (ep->type == 0x0) { ++ return TYPE_UNUSED; ++ } else if (ep->type < 0x80) { ++ return TYPE_DELETED; ++ } else if (ep->type == 0x80) { ++ return TYPE_INVALID; ++ } else if (ep->type < 0xA0) { ++ if (ep->type == 0x81) { ++ return TYPE_BITMAP; ++ } else if (ep->type == 0x82) { ++ return TYPE_UPCASE; ++ } else if (ep->type == 0x83) { ++ return TYPE_VOLUME; ++ } else if (ep->type == 0x85) { ++ if (GET16_A(ep->attr) & ATTR_SUBDIR) ++ return TYPE_DIR; ++ else ++ return TYPE_FILE; ++ } ++ return TYPE_CRITICAL_PRI; ++ } else if (ep->type < 0xC0) { ++ if (ep->type == 0xA0) { ++ return TYPE_GUID; ++ } else if (ep->type == 0xA1) { ++ return TYPE_PADDING; ++ } else if (ep->type == 0xA2) { ++ return TYPE_ACLTAB; ++ } ++ return TYPE_BENIGN_PRI; ++ } else if (ep->type < 0xE0) { ++ if (ep->type == 0xC0) { ++ return TYPE_STREAM; ++ } else if (ep->type == 0xC1) { ++ return TYPE_EXTEND; ++ } else if (ep->type == 0xC2) { ++ return TYPE_ACL; ++ } ++ return TYPE_CRITICAL_SEC; ++ } ++ ++ return TYPE_BENIGN_SEC; ++} ++ ++void fat_set_entry_type(DENTRY_T *p_entry, UINT32 type) ++{ ++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; ++ ++ if (type == TYPE_UNUSED) ++ *(ep->name) = 0x0; ++ ++ else if (type == TYPE_DELETED) ++ *(ep->name) = 0xE5; ++ ++ else if (type == TYPE_EXTEND) ++ ep->attr = ATTR_EXTEND; ++ ++ else if (type == TYPE_DIR) ++ ep->attr = ATTR_SUBDIR; ++ ++ else if (type == TYPE_FILE) ++ ep->attr = ATTR_ARCHIVE; ++ ++ else if (type == TYPE_SYMLINK) ++ ep->attr = ATTR_ARCHIVE | ATTR_SYMLINK; ++} ++ ++void exfat_set_entry_type(DENTRY_T *p_entry, UINT32 type) ++{ ++ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; ++ ++ if (type == TYPE_UNUSED) { ++ ep->type = 0x0; ++ } else if (type == TYPE_DELETED) { ++ ep->type &= ~0x80; ++ } else if (type == TYPE_STREAM) { ++ ep->type = 0xC0; ++ } else if (type == TYPE_EXTEND) { ++ ep->type = 0xC1; ++ } else if (type == TYPE_BITMAP) { ++ ep->type = 0x81; ++ } else if (type == TYPE_UPCASE) { ++ ep->type = 0x82; ++ } else if (type == TYPE_VOLUME) { ++ ep->type = 0x83; ++ } else if (type == TYPE_DIR) { ++ ep->type = 0x85; ++ SET16_A(ep->attr, ATTR_SUBDIR); ++ } else if (type == TYPE_FILE) { ++ ep->type = 0x85; ++ SET16_A(ep->attr, ATTR_ARCHIVE); ++ } else if (type == TYPE_SYMLINK) { ++ ep->type = 0x85; ++ SET16_A(ep->attr, ATTR_ARCHIVE | ATTR_SYMLINK); ++ } ++} ++ ++UINT32 fat_get_entry_attr(DENTRY_T *p_entry) ++{ ++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; ++ return((UINT32) ep->attr); ++} ++ ++UINT32 exfat_get_entry_attr(DENTRY_T *p_entry) ++{ ++ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; ++ return((UINT32) GET16_A(ep->attr)); ++} ++ ++void fat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr) ++{ ++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; ++ ep->attr = (UINT8) attr; ++} ++ ++void exfat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr) ++{ ++ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; ++ SET16_A(ep->attr, (UINT16) attr); ++} ++ ++UINT8 fat_get_entry_flag(DENTRY_T *p_entry) ++{ ++ return 0x01; ++} ++ ++UINT8 exfat_get_entry_flag(DENTRY_T *p_entry) ++{ ++ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; ++ return(ep->flags); ++} ++ ++void fat_set_entry_flag(DENTRY_T *p_entry, UINT8 flags) ++{ ++} ++ ++void exfat_set_entry_flag(DENTRY_T *p_entry, UINT8 flags) ++{ ++ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; ++ ep->flags = flags; ++} ++ ++UINT32 fat_get_entry_clu0(DENTRY_T *p_entry) ++{ ++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; ++ return((GET32_A(ep->start_clu_hi) << 16) | GET16_A(ep->start_clu_lo)); ++} ++ ++UINT32 exfat_get_entry_clu0(DENTRY_T *p_entry) ++{ ++ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; ++ return(GET32_A(ep->start_clu)); ++} ++ ++void fat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu) ++{ ++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; ++ SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu)); ++ SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16)); ++} ++ ++void exfat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu) ++{ ++ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; ++ SET32_A(ep->start_clu, start_clu); ++} ++ ++UINT64 fat_get_entry_size(DENTRY_T *p_entry) ++{ ++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; ++ return((UINT64) GET32_A(ep->size)); ++} ++ ++UINT64 exfat_get_entry_size(DENTRY_T *p_entry) ++{ ++ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; ++ return(GET64_A(ep->valid_size)); ++} ++ ++void fat_set_entry_size(DENTRY_T *p_entry, UINT64 size) ++{ ++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; ++ SET32_A(ep->size, (UINT32) size); ++} ++ ++void exfat_set_entry_size(DENTRY_T *p_entry, UINT64 size) ++{ ++ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; ++ SET64_A(ep->valid_size, size); ++ SET64_A(ep->size, size); ++} ++ ++void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) ++{ ++ UINT16 t = 0x00, d = 0x21; ++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; ++ ++ switch (mode) { ++ case TM_CREATE: ++ t = GET16_A(ep->create_time); ++ d = GET16_A(ep->create_date); ++ break; ++ case TM_MODIFY: ++ t = GET16_A(ep->modify_time); ++ d = GET16_A(ep->modify_date); ++ break; ++ } ++ ++ tp->sec = (t & 0x001F) << 1; ++ tp->min = (t >> 5) & 0x003F; ++ tp->hour = (t >> 11); ++ tp->day = (d & 0x001F); ++ tp->mon = (d >> 5) & 0x000F; ++ tp->year = (d >> 9); ++} ++ ++void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) ++{ ++ UINT16 t = 0x00, d = 0x21; ++ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; ++ ++ switch (mode) { ++ case TM_CREATE: ++ t = GET16_A(ep->create_time); ++ d = GET16_A(ep->create_date); ++ break; ++ case TM_MODIFY: ++ t = GET16_A(ep->modify_time); ++ d = GET16_A(ep->modify_date); ++ break; ++ case TM_ACCESS: ++ t = GET16_A(ep->access_time); ++ d = GET16_A(ep->access_date); ++ break; ++ } ++ ++ tp->sec = (t & 0x001F) << 1; ++ tp->min = (t >> 5) & 0x003F; ++ tp->hour = (t >> 11); ++ tp->day = (d & 0x001F); ++ tp->mon = (d >> 5) & 0x000F; ++ tp->year = (d >> 9); ++} ++ ++void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) ++{ ++ UINT16 t, d; ++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; ++ ++ t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); ++ d = (tp->year << 9) | (tp->mon << 5) | tp->day; ++ ++ switch (mode) { ++ case TM_CREATE: ++ SET16_A(ep->create_time, t); ++ SET16_A(ep->create_date, d); ++ break; ++ case TM_MODIFY: ++ SET16_A(ep->modify_time, t); ++ SET16_A(ep->modify_date, d); ++ break; ++ } ++} ++ ++void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) ++{ ++ UINT16 t, d; ++ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; ++ ++ t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); ++ d = (tp->year << 9) | (tp->mon << 5) | tp->day; ++ ++ switch (mode) { ++ case TM_CREATE: ++ SET16_A(ep->create_time, t); ++ SET16_A(ep->create_date, d); ++ break; ++ case TM_MODIFY: ++ SET16_A(ep->modify_time, t); ++ SET16_A(ep->modify_date, d); ++ break; ++ case TM_ACCESS: ++ SET16_A(ep->access_time, t); ++ SET16_A(ep->access_date, d); ++ break; ++ } ++} ++ ++INT32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, ++ UINT32 start_clu, UINT64 size) ++{ ++ UINT32 sector; ++ DOS_DENTRY_T *dos_ep; ++ ++ dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); ++ if (!dos_ep) ++ return FFS_MEDIAERR; ++ ++ init_dos_entry(dos_ep, type, start_clu); ++ buf_modify(sb, sector); ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, ++ UINT32 start_clu, UINT64 size) ++{ ++ UINT32 sector; ++ UINT8 flags; ++ FILE_DENTRY_T *file_ep; ++ STRM_DENTRY_T *strm_ep; ++ ++ flags = (type == TYPE_FILE) ? 0x01 : 0x03; ++ ++ file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); ++ if (!file_ep) ++ return FFS_MEDIAERR; ++ ++ strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, §or); ++ if (!strm_ep) ++ return FFS_MEDIAERR; ++ ++ init_file_entry(file_ep, type); ++ buf_modify(sb, sector); ++ ++ init_strm_entry(strm_ep, flags, start_clu, size); ++ buf_modify(sb, sector); ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 fat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, ++ UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) ++{ ++ INT32 i; ++ UINT32 sector; ++ UINT8 chksum; ++ UINT16 *uniname = p_uniname->name; ++ DOS_DENTRY_T *dos_ep; ++ EXT_DENTRY_T *ext_ep; ++ ++ dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); ++ if (!dos_ep) ++ return FFS_MEDIAERR; ++ ++ dos_ep->lcase = p_dosname->name_case; ++ MEMCPY(dos_ep->name, p_dosname->name, DOS_NAME_LENGTH); ++ buf_modify(sb, sector); ++ ++ if ((--num_entries) > 0) { ++ chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); ++ ++ for (i = 1; i < num_entries; i++) { ++ ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); ++ if (!ext_ep) ++ return FFS_MEDIAERR; ++ ++ init_ext_entry(ext_ep, i, chksum, uniname); ++ buf_modify(sb, sector); ++ uniname += 13; ++ } ++ ++ ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); ++ if (!ext_ep) ++ return FFS_MEDIAERR; ++ ++ init_ext_entry(ext_ep, i+0x40, chksum, uniname); ++ buf_modify(sb, sector); ++ } ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 exfat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, ++ UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) ++{ ++ INT32 i; ++ UINT32 sector; ++ UINT16 *uniname = p_uniname->name; ++ FILE_DENTRY_T *file_ep; ++ STRM_DENTRY_T *strm_ep; ++ NAME_DENTRY_T *name_ep; ++ ++ file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); ++ if (!file_ep) ++ return FFS_MEDIAERR; ++ ++ file_ep->num_ext = (UINT8)(num_entries - 1); ++ buf_modify(sb, sector); ++ ++ strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, §or); ++ if (!strm_ep) ++ return FFS_MEDIAERR; ++ ++ strm_ep->name_len = p_uniname->name_len; ++ SET16_A(strm_ep->name_hash, p_uniname->name_hash); ++ buf_modify(sb, sector); ++ ++ for (i = 2; i < num_entries; i++) { ++ name_ep = (NAME_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+i, §or); ++ if (!name_ep) ++ return FFS_MEDIAERR; ++ ++ init_name_entry(name_ep, uniname); ++ buf_modify(sb, sector); ++ uniname += 15; ++ } ++ ++ update_dir_checksum(sb, p_dir, entry); ++ ++ return FFS_SUCCESS; ++} ++ ++void init_dos_entry(DOS_DENTRY_T *ep, UINT32 type, UINT32 start_clu) ++{ ++ TIMESTAMP_T tm, *tp; ++ ++ fat_set_entry_type((DENTRY_T *) ep, type); ++ SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu)); ++ SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16)); ++ SET32_A(ep->size, 0); ++ ++ tp = tm_current(&tm); ++ fat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); ++ fat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); ++ SET16_A(ep->access_date, 0); ++ ep->create_time_ms = 0; ++} ++ ++void init_ext_entry(EXT_DENTRY_T *ep, INT32 order, UINT8 chksum, UINT16 *uniname) ++{ ++ INT32 i; ++ UINT8 end = FALSE; ++ ++ fat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); ++ ep->order = (UINT8) order; ++ ep->sysid = 0; ++ ep->checksum = chksum; ++ SET16_A(ep->start_clu, 0); ++ ++ for (i = 0; i < 10; i += 2) { ++ if (!end) { ++ SET16(ep->unicode_0_4+i, *uniname); ++ if (*uniname == 0x0) ++ end = TRUE; ++ else ++ uniname++; ++ } else { ++ SET16(ep->unicode_0_4+i, 0xFFFF); ++ } ++ } ++ ++ for (i = 0; i < 12; i += 2) { ++ if (!end) { ++ SET16_A(ep->unicode_5_10+i, *uniname); ++ if (*uniname == 0x0) ++ end = TRUE; ++ else ++ uniname++; ++ } else { ++ SET16_A(ep->unicode_5_10+i, 0xFFFF); ++ } ++ } ++ ++ for (i = 0; i < 4; i += 2) { ++ if (!end) { ++ SET16_A(ep->unicode_11_12+i, *uniname); ++ if (*uniname == 0x0) ++ end = TRUE; ++ else ++ uniname++; ++ } else { ++ SET16_A(ep->unicode_11_12+i, 0xFFFF); ++ } ++ } ++} ++ ++void init_file_entry(FILE_DENTRY_T *ep, UINT32 type) ++{ ++ TIMESTAMP_T tm, *tp; ++ ++ exfat_set_entry_type((DENTRY_T *) ep, type); ++ ++ tp = tm_current(&tm); ++ exfat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); ++ exfat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); ++ exfat_set_entry_time((DENTRY_T *) ep, tp, TM_ACCESS); ++ ep->create_time_ms = 0; ++ ep->modify_time_ms = 0; ++ ep->access_time_ms = 0; ++} ++ ++void init_strm_entry(STRM_DENTRY_T *ep, UINT8 flags, UINT32 start_clu, UINT64 size) ++{ ++ exfat_set_entry_type((DENTRY_T *) ep, TYPE_STREAM); ++ ep->flags = flags; ++ SET32_A(ep->start_clu, start_clu); ++ SET64_A(ep->valid_size, size); ++ SET64_A(ep->size, size); ++} ++ ++void init_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname) ++{ ++ INT32 i; ++ ++ exfat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); ++ ep->flags = 0x0; ++ ++ for (i = 0; i < 30; i++, i++) { ++ SET16_A(ep->unicode_0_14+i, *uniname); ++ if (*uniname == 0x0) ++ break; ++ uniname++; ++ } ++} ++ ++void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries) ++{ ++ INT32 i; ++ UINT32 sector; ++ DENTRY_T *ep; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ for (i = num_entries-1; i >= order; i--) { ++ ep = get_entry_in_dir(sb, p_dir, entry-i, §or); ++ if (!ep) ++ return; ++ ++ p_fs->fs_func->set_entry_type(ep, TYPE_DELETED); ++ buf_modify(sb, sector); ++ } ++} ++ ++void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries) ++{ ++ INT32 i; ++ UINT32 sector; ++ DENTRY_T *ep; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ for (i = order; i < num_entries; i++) { ++ ep = get_entry_in_dir(sb, p_dir, entry+i, §or); ++ if (!ep) ++ return; ++ ++ p_fs->fs_func->set_entry_type(ep, TYPE_DELETED); ++ buf_modify(sb, sector); ++ } ++} ++ ++void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, INT32 entry) ++{ ++ INT32 i, num_entries; ++ UINT32 sector; ++ UINT16 chksum; ++ FILE_DENTRY_T *file_ep; ++ DENTRY_T *ep; ++ ++ file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); ++ if (!file_ep) ++ return; ++ ++ buf_lock(sb, sector); ++ ++ num_entries = (INT32) file_ep->num_ext + 1; ++ chksum = calc_checksum_2byte((void *) file_ep, DENTRY_SIZE, 0, CS_DIR_ENTRY); ++ ++ for (i = 1; i < num_entries; i++) { ++ ep = get_entry_in_dir(sb, p_dir, entry+i, NULL); ++ if (!ep) { ++ buf_unlock(sb, sector); ++ return; ++ } ++ ++ chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, CS_DEFAULT); ++ } ++ ++ SET16_A(file_ep->checksum, chksum); ++ buf_modify(sb, sector); ++ buf_unlock(sb, sector); ++} ++ ++void update_dir_checksum_with_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es) ++{ ++ DENTRY_T *ep; ++ UINT16 chksum = 0; ++ INT32 chksum_type = CS_DIR_ENTRY, i; ++ ++ ep = (DENTRY_T *)&(es->__buf); ++ for (i=0; i < es->num_entries; i++) { ++ PRINTK ("update_dir_checksum_with_entry_set ep %p\n", ep); ++ chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, chksum_type); ++ ep++; ++ chksum_type = CS_DEFAULT; ++ } ++ ++ ep = (DENTRY_T *)&(es->__buf); ++ SET16_A(((FILE_DENTRY_T *)ep)->checksum, chksum); ++ write_whole_entry_set(sb, es); ++} ++ ++static INT32 _walk_fat_chain (struct super_block *sb, CHAIN_T *p_dir, INT32 byte_offset, UINT32 *clu) ++{ ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ INT32 clu_offset; ++ UINT32 cur_clu; ++ ++ clu_offset = byte_offset >> p_fs->cluster_size_bits; ++ cur_clu = p_dir->dir; ++ ++ if (p_dir->flags == 0x03) { ++ cur_clu += clu_offset; ++ } else { ++ while (clu_offset > 0) { ++ if (FAT_read(sb, cur_clu, &cur_clu) == -1) ++ return FFS_MEDIAERR; ++ clu_offset--; ++ } ++ } ++ ++ if (clu) ++ *clu = cur_clu; ++ return FFS_SUCCESS; ++} ++INT32 find_location(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector, INT32 *offset) ++{ ++ INT32 off, ret; ++ UINT32 clu=0; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ off = entry << DENTRY_SIZE_BITS; ++ ++ if (p_dir->dir == CLUSTER_32(0)) { ++ *offset = off & p_bd->sector_size_mask; ++ *sector = off >> p_bd->sector_size_bits; ++ *sector += p_fs->root_start_sector; ++ } else { ++ ret =_walk_fat_chain(sb, p_dir, off, &clu); ++ if (ret != FFS_SUCCESS) ++ return ret; ++ ++ off &= p_fs->cluster_size - 1; ++ ++ *offset = off & p_bd->sector_size_mask; ++ *sector = off >> p_bd->sector_size_bits; ++ *sector += START_SECTOR(clu); ++ } ++ return FFS_SUCCESS; ++} ++ ++DENTRY_T *get_entry_with_sector(struct super_block *sb, UINT32 sector, INT32 offset) ++{ ++ UINT8 *buf; ++ ++ buf = buf_getblk(sb, sector); ++ ++ if (buf == NULL) ++ return NULL; ++ ++ return((DENTRY_T *)(buf + offset)); ++} ++ ++DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector) ++{ ++ INT32 off; ++ UINT32 sec; ++ UINT8 *buf; ++ ++ if (find_location(sb, p_dir, entry, &sec, &off) != FFS_SUCCESS) ++ return NULL; ++ ++ buf = buf_getblk(sb, sec); ++ ++ if (buf == NULL) ++ return NULL; ++ ++ if (sector != NULL) ++ *sector = sec; ++ return((DENTRY_T *)(buf + off)); ++} ++ ++#define ES_MODE_STARTED 0 ++#define ES_MODE_GET_FILE_ENTRY 1 ++#define ES_MODE_GET_STRM_ENTRY 2 ++#define ES_MODE_GET_NAME_ENTRY 3 ++#define ES_MODE_GET_CRITICAL_SEC_ENTRY 4 ++ENTRY_SET_CACHE_T *get_entry_set_in_dir (struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, DENTRY_T **file_ep) ++{ ++ INT32 off, ret, byte_offset; ++ UINT32 clu=0; ++ UINT32 sec, entry_type; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ENTRY_SET_CACHE_T *es = NULL; ++ DENTRY_T *ep, *pos; ++ UINT8 *buf; ++ UINT8 num_entries; ++ INT32 mode = ES_MODE_STARTED; ++ ++ PRINTK("get_entry_set_in_dir entered\n"); ++ PRINTK("p_dir dir %u flags %x size %d\n", p_dir->dir, p_dir->flags, p_dir->size); ++ ++ byte_offset = entry << DENTRY_SIZE_BITS; ++ ret =_walk_fat_chain(sb, p_dir, byte_offset, &clu); ++ if (ret != FFS_SUCCESS) ++ return NULL; ++ ++ ++ byte_offset &= p_fs->cluster_size - 1; ++ ++ off = byte_offset & p_bd->sector_size_mask; ++ sec = byte_offset >> p_bd->sector_size_bits; ++ sec += START_SECTOR(clu); ++ ++ buf = buf_getblk(sb, sec); ++ if (buf == NULL) ++ goto err_out; ++ ++ ++ ep = (DENTRY_T *)(buf + off); ++ entry_type = p_fs->fs_func->get_entry_type(ep); ++ ++ if ((entry_type != TYPE_FILE) ++ && (entry_type != TYPE_DIR)) ++ goto err_out; ++ ++ if (type == ES_ALL_ENTRIES) ++ num_entries = ((FILE_DENTRY_T *)ep)->num_ext+1; ++ else ++ num_entries = type; ++ ++ PRINTK("trying to malloc %x bytes for %d entries\n", offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T), num_entries); ++ es = MALLOC(offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T)); ++ if (es == NULL) ++ goto err_out; ++ ++ es->num_entries = num_entries; ++ es->sector = sec; ++ es->offset = off; ++ es->alloc_flag = p_dir->flags; ++ ++ pos = (DENTRY_T *) &(es->__buf); ++ ++ while(num_entries) { ++ ++ entry_type = p_fs->fs_func->get_entry_type(ep); ++ ++ if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) ++ goto err_out; ++ ++ switch(mode) { ++ case ES_MODE_STARTED: ++ if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) ++ mode = ES_MODE_GET_FILE_ENTRY; ++ else ++ goto err_out; ++ break; ++ case ES_MODE_GET_FILE_ENTRY: ++ if (entry_type == TYPE_STREAM) ++ mode = ES_MODE_GET_STRM_ENTRY; ++ else ++ goto err_out; ++ break; ++ case ES_MODE_GET_STRM_ENTRY: ++ if (entry_type == TYPE_EXTEND) ++ mode = ES_MODE_GET_NAME_ENTRY; ++ else ++ goto err_out; ++ break; ++ case ES_MODE_GET_NAME_ENTRY: ++ if (entry_type == TYPE_EXTEND) ++ break; ++ else if (entry_type == TYPE_STREAM) ++ goto err_out; ++ else if (entry_type & TYPE_CRITICAL_SEC) ++ mode = ES_MODE_GET_CRITICAL_SEC_ENTRY; ++ else ++ goto err_out; ++ break; ++ case ES_MODE_GET_CRITICAL_SEC_ENTRY: ++ if ((entry_type == TYPE_EXTEND) || (entry_type == TYPE_STREAM)) ++ goto err_out; ++ else if ((entry_type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC) ++ goto err_out; ++ break; ++ } ++ ++ COPY_DENTRY(pos, ep); ++ ++ if (--num_entries == 0) ++ break; ++ ++ if (((off + DENTRY_SIZE) & p_bd->sector_size_mask) < (off & p_bd->sector_size_mask)) { ++ if (IS_LAST_SECTOR_IN_CLUSTER(sec)) { ++ if (es->alloc_flag == 0x03) { ++ clu++; ++ } else { ++ if (FAT_read(sb, clu, &clu) == -1) ++ goto err_out; ++ } ++ sec = START_SECTOR(clu); ++ } else { ++ sec++; ++ } ++ buf = buf_getblk(sb, sec); ++ if (buf == NULL) ++ goto err_out; ++ off = 0; ++ ep = (DENTRY_T *)(buf); ++ } else { ++ ep++; ++ off += DENTRY_SIZE; ++ } ++ pos++; ++ } ++ ++ if (file_ep) ++ *file_ep = (DENTRY_T *)&(es->__buf); ++ ++ PRINTK("es sec %u offset %d flags %d, num_entries %u buf ptr %p\n", ++ es->sector, es->offset, es->alloc_flag, es->num_entries, &(es->__buf)); ++ PRINTK("get_entry_set_in_dir exited %p\n", es); ++ return es; ++err_out: ++ PRINTK("get_entry_set_in_dir exited NULL (es %p)\n", es); ++ if (es) ++ FREE(es); ++ return NULL; ++} ++ ++void release_entry_set (ENTRY_SET_CACHE_T *es) ++{ ++ PRINTK("release_entry_set %p\n", es); ++ FREE(es); ++} ++ ++ ++static INT32 __write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, UINT32 sec, INT32 off, UINT32 count) ++{ ++ INT32 num_entries, buf_off = (off - es->offset); ++ UINT32 remaining_byte_in_sector, copy_entries; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ UINT32 clu; ++ UINT8 *buf, *esbuf = (UINT8 *)&(es->__buf); ++ ++ PRINTK("__write_partial_entries_in_entry_set entered\n"); ++ PRINTK("es %p sec %u off %d count %d\n", es, sec, off, count); ++ num_entries = count; ++ ++ while(num_entries) { ++ remaining_byte_in_sector = (1 << p_bd->sector_size_bits) - off; ++ copy_entries = MIN(remaining_byte_in_sector>> DENTRY_SIZE_BITS , num_entries); ++ buf = buf_getblk(sb, sec); ++ if (buf == NULL) ++ goto err_out; ++ PRINTK("es->buf %p buf_off %u\n", esbuf, buf_off); ++ PRINTK("copying %d entries from %p to sector %u\n", copy_entries, (esbuf + buf_off), sec); ++ MEMCPY(buf + off, esbuf + buf_off, copy_entries << DENTRY_SIZE_BITS); ++ buf_modify(sb, sec); ++ num_entries -= copy_entries; ++ ++ if (num_entries) { ++ if (IS_LAST_SECTOR_IN_CLUSTER(sec)) { ++ clu = GET_CLUSTER_FROM_SECTOR(sec); ++ if (es->alloc_flag == 0x03) { ++ clu++; ++ } else { ++ if (FAT_read(sb, clu, &clu) == -1) ++ goto err_out; ++ } ++ sec = START_SECTOR(clu); ++ } else { ++ sec++; ++ } ++ off = 0; ++ buf_off += copy_entries << DENTRY_SIZE_BITS; ++ } ++ } ++ ++ PRINTK("__write_partial_entries_in_entry_set exited successfully\n"); ++ return FFS_SUCCESS; ++err_out: ++ PRINTK("__write_partial_entries_in_entry_set failed\n"); ++ return FFS_ERROR; ++} ++ ++INT32 write_whole_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es) ++{ ++ return (__write_partial_entries_in_entry_set(sb, es, es->sector,es->offset, es->num_entries)); ++} ++ ++INT32 write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, UINT32 count) ++{ ++ INT32 ret, byte_offset, off; ++ UINT32 clu=0, sec; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ CHAIN_T dir; ++ ++ if (ep + count > ((DENTRY_T *)&(es->__buf)) + es->num_entries) ++ return FFS_ERROR; ++ ++ dir.dir = GET_CLUSTER_FROM_SECTOR(es->sector); ++ dir.flags = es->alloc_flag; ++ dir.size = 0xffffffff; ++ ++ byte_offset = (es->sector - START_SECTOR(dir.dir)) << p_bd->sector_size_bits; ++ byte_offset += ((INT32)ep - (INT32)&(es->__buf)) + es->offset; ++ ++ ret =_walk_fat_chain(sb, &dir, byte_offset, &clu); ++ if (ret != FFS_SUCCESS) ++ return ret; ++ byte_offset &= p_fs->cluster_size - 1; ++ off = byte_offset & p_bd->sector_size_mask; ++ sec = byte_offset >> p_bd->sector_size_bits; ++ sec += START_SECTOR(clu); ++ return (__write_partial_entries_in_entry_set(sb, es, sec, off, count)); ++} ++ ++INT32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 num_entries) ++{ ++ INT32 i, dentry, num_empty = 0; ++ INT32 dentries_per_clu; ++ UINT32 type; ++ CHAIN_T clu; ++ DENTRY_T *ep; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ dentries_per_clu = p_fs->dentries_in_root; ++ else ++ dentries_per_clu = p_fs->dentries_per_clu; ++ ++ if (p_fs->hint_uentry.dir == p_dir->dir) { ++ if (p_fs->hint_uentry.entry == -1) ++ return -1; ++ ++ clu.dir = p_fs->hint_uentry.clu.dir; ++ clu.size = p_fs->hint_uentry.clu.size; ++ clu.flags = p_fs->hint_uentry.clu.flags; ++ ++ dentry = p_fs->hint_uentry.entry; ++ } else { ++ p_fs->hint_uentry.entry = -1; ++ ++ clu.dir = p_dir->dir; ++ clu.size = p_dir->size; ++ clu.flags = p_dir->flags; ++ ++ dentry = 0; ++ } ++ ++ while (clu.dir != CLUSTER_32(~0)) { ++ if (p_fs->dev_ejected) ++ break; ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ i = dentry % dentries_per_clu; ++ else ++ i = dentry & (dentries_per_clu-1); ++ ++ for ( ; i < dentries_per_clu; i++, dentry++) { ++ ep = get_entry_in_dir(sb, &clu, i, NULL); ++ if (!ep) ++ return -1; ++ ++ type = p_fs->fs_func->get_entry_type(ep); ++ ++ if (type == TYPE_UNUSED) { ++ num_empty++; ++ if (p_fs->hint_uentry.entry == -1) { ++ p_fs->hint_uentry.dir = p_dir->dir; ++ p_fs->hint_uentry.entry = dentry; ++ ++ p_fs->hint_uentry.clu.dir = clu.dir; ++ p_fs->hint_uentry.clu.size = clu.size; ++ p_fs->hint_uentry.clu.flags = clu.flags; ++ } ++ } else if (type == TYPE_DELETED) { ++ num_empty++; ++ } else { ++ num_empty = 0; ++ } ++ ++ if (num_empty >= num_entries) { ++ p_fs->hint_uentry.dir = CLUSTER_32(~0); ++ p_fs->hint_uentry.entry = -1; ++ ++ if (p_fs->vol_type == EXFAT) ++ return(dentry - (num_entries-1)); ++ else ++ return(dentry); ++ } ++ } ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ break; ++ ++ if (clu.flags == 0x03) { ++ if ((--clu.size) > 0) ++ clu.dir++; ++ else ++ clu.dir = CLUSTER_32(~0); ++ } else { ++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) ++ return -1; ++ } ++ } ++ ++ return -1; ++} ++ ++INT32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, INT32 num_entries) ++{ ++ INT32 ret, dentry; ++ UINT32 last_clu, sector; ++ UINT64 size = 0; ++ CHAIN_T clu; ++ DENTRY_T *ep = NULL; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ return(search_deleted_or_unused_entry(sb, p_dir, num_entries)); ++ ++ while ((dentry = search_deleted_or_unused_entry(sb, p_dir, num_entries)) < 0) { ++ if (p_fs->dev_ejected) ++ break; ++ ++ if (p_fs->vol_type == EXFAT) { ++ if (p_dir->dir != p_fs->root_dir) { ++ size = i_size_read(inode); ++ } ++ } ++ ++ last_clu = find_last_cluster(sb, p_dir); ++ clu.dir = last_clu + 1; ++ clu.size = 0; ++ clu.flags = p_dir->flags; ++ ++ ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu); ++ if (ret < 1) ++ return -1; ++ ++ if (clear_cluster(sb, clu.dir) != FFS_SUCCESS) ++ return -1; ++ ++ if (clu.flags != p_dir->flags) { ++ exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size); ++ p_dir->flags = 0x01; ++ p_fs->hint_uentry.clu.flags = 0x01; ++ } ++ if (clu.flags == 0x01) ++ FAT_write(sb, last_clu, clu.dir); ++ ++ if (p_fs->hint_uentry.entry == -1) { ++ p_fs->hint_uentry.dir = p_dir->dir; ++ p_fs->hint_uentry.entry = p_dir->size << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); ++ ++ p_fs->hint_uentry.clu.dir = clu.dir; ++ p_fs->hint_uentry.clu.size = 0; ++ p_fs->hint_uentry.clu.flags = clu.flags; ++ } ++ p_fs->hint_uentry.clu.size++; ++ p_dir->size++; ++ ++ if (p_fs->vol_type == EXFAT) { ++ if (p_dir->dir != p_fs->root_dir) { ++ size += p_fs->cluster_size; ++ ++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry+1, §or); ++ if (!ep) ++ return -1; ++ p_fs->fs_func->set_entry_size(ep, size); ++ p_fs->fs_func->set_entry_flag(ep, p_dir->flags); ++ buf_modify(sb, sector); ++ ++ update_dir_checksum(sb, &(fid->dir), fid->entry); ++ } ++ } ++ ++ i_size_write(inode, i_size_read(inode)+p_fs->cluster_size); ++ EXFAT_I(inode)->mmu_private += p_fs->cluster_size; ++ EXFAT_I(inode)->fid.size += p_fs->cluster_size; ++ EXFAT_I(inode)->fid.flags = p_dir->flags; ++ inode->i_blocks += 1 << (p_fs->cluster_size_bits - 9); ++ } ++ ++ return(dentry); ++} ++ ++INT32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type) ++{ ++ INT32 i, dentry = 0, lossy = FALSE, len; ++ INT32 order = 0, is_feasible_entry = TRUE, has_ext_entry = FALSE; ++ INT32 dentries_per_clu; ++ UINT32 entry_type; ++ UINT16 entry_uniname[14], *uniname = NULL, unichar; ++ CHAIN_T clu; ++ DENTRY_T *ep; ++ DOS_DENTRY_T *dos_ep; ++ EXT_DENTRY_T *ext_ep; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (p_dir->dir == p_fs->root_dir) { ++ if ((!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_CUR_DIR_NAME)) || ++ (!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_PAR_DIR_NAME))) ++ return -1; ++ } ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ dentries_per_clu = p_fs->dentries_in_root; ++ else ++ dentries_per_clu = p_fs->dentries_per_clu; ++ ++ clu.dir = p_dir->dir; ++ clu.flags = p_dir->flags; ++ ++ while (clu.dir != CLUSTER_32(~0)) { ++ if (p_fs->dev_ejected) ++ break; ++ ++ for (i = 0; i < dentries_per_clu; i++, dentry++) { ++ ep = get_entry_in_dir(sb, &clu, i, NULL); ++ if (!ep) ++ return -2; ++ ++ entry_type = p_fs->fs_func->get_entry_type(ep); ++ ++ if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) { ++ if ((type == TYPE_ALL) || (type == entry_type)) { ++ if (is_feasible_entry && has_ext_entry) ++ return(dentry); ++ ++ dos_ep = (DOS_DENTRY_T *) ep; ++ if ((!lossy) && (!nls_dosname_cmp(sb, p_dosname->name, dos_ep->name))) ++ return(dentry); ++ } ++ is_feasible_entry = TRUE; ++ has_ext_entry = FALSE; ++ } else if (entry_type == TYPE_EXTEND) { ++ if (is_feasible_entry) { ++ ext_ep = (EXT_DENTRY_T *) ep; ++ if (ext_ep->order > 0x40) { ++ order = (INT32)(ext_ep->order - 0x40); ++ uniname = p_uniname->name + 13 * (order-1); ++ } else { ++ order = (INT32) ext_ep->order; ++ uniname -= 13; ++ } ++ ++ len = extract_uni_name_from_ext_entry(ext_ep, entry_uniname, order); ++ ++ unichar = *(uniname+len); ++ *(uniname+len) = 0x0; ++ ++ if (nls_uniname_cmp(sb, uniname, entry_uniname)) { ++ is_feasible_entry = FALSE; ++ } ++ ++ *(uniname+len) = unichar; ++ } ++ has_ext_entry = TRUE; ++ } else if (entry_type == TYPE_UNUSED) { ++ return -2; ++ } else { ++ is_feasible_entry = TRUE; ++ has_ext_entry = FALSE; ++ } ++ } ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ break; ++ ++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) ++ return -2; ++ } ++ ++ return -2; ++} ++ ++INT32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type) ++{ ++ INT32 i, dentry = 0, num_ext_entries = 0, len; ++ INT32 order = 0, is_feasible_entry = FALSE; ++ INT32 dentries_per_clu, num_empty = 0; ++ UINT32 entry_type; ++ UINT16 entry_uniname[16], *uniname = NULL, unichar; ++ CHAIN_T clu; ++ DENTRY_T *ep; ++ FILE_DENTRY_T *file_ep; ++ STRM_DENTRY_T *strm_ep; ++ NAME_DENTRY_T *name_ep; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (p_dir->dir == p_fs->root_dir) { ++ if ((!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_CUR_DIR_NAME)) || ++ (!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_PAR_DIR_NAME))) ++ return -1; ++ } ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ dentries_per_clu = p_fs->dentries_in_root; ++ else ++ dentries_per_clu = p_fs->dentries_per_clu; ++ ++ clu.dir = p_dir->dir; ++ clu.size = p_dir->size; ++ clu.flags = p_dir->flags; ++ ++ p_fs->hint_uentry.dir = p_dir->dir; ++ p_fs->hint_uentry.entry = -1; ++ ++ while (clu.dir != CLUSTER_32(~0)) { ++ if (p_fs->dev_ejected) ++ break; ++ ++ for (i = 0; i < dentries_per_clu; i++, dentry++) { ++ ep = get_entry_in_dir(sb, &clu, i, NULL); ++ if (!ep) ++ return -2; ++ ++ entry_type = p_fs->fs_func->get_entry_type(ep); ++ ++ if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) { ++ is_feasible_entry = FALSE; ++ ++ if (p_fs->hint_uentry.entry == -1) { ++ num_empty++; ++ ++ if (num_empty == 1) { ++ p_fs->hint_uentry.clu.dir = clu.dir; ++ p_fs->hint_uentry.clu.size = clu.size; ++ p_fs->hint_uentry.clu.flags = clu.flags; ++ } ++ if ((num_empty >= num_entries) || (entry_type == TYPE_UNUSED)) { ++ p_fs->hint_uentry.entry = dentry - (num_empty-1); ++ } ++ } ++ ++ if (entry_type == TYPE_UNUSED) { ++ return -2; ++ } ++ } else { ++ num_empty = 0; ++ ++ if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) { ++ if ((type == TYPE_ALL) || (type == entry_type)) { ++ file_ep = (FILE_DENTRY_T *) ep; ++ num_ext_entries = file_ep->num_ext; ++ is_feasible_entry = TRUE; ++ } else { ++ is_feasible_entry = FALSE; ++ } ++ } else if (entry_type == TYPE_STREAM) { ++ if (is_feasible_entry) { ++ strm_ep = (STRM_DENTRY_T *) ep; ++ if ((p_uniname->name_hash == GET16_A(strm_ep->name_hash)) && ++ (p_uniname->name_len == strm_ep->name_len)) { ++ order = 1; ++ } else { ++ is_feasible_entry = FALSE; ++ } ++ } ++ } else if (entry_type == TYPE_EXTEND) { ++ if (is_feasible_entry) { ++ name_ep = (NAME_DENTRY_T *) ep; ++ ++ if ((++order) == 2) ++ uniname = p_uniname->name; ++ else ++ uniname += 15; ++ ++ len = extract_uni_name_from_name_entry(name_ep, entry_uniname, order); ++ ++ unichar = *(uniname+len); ++ *(uniname+len) = 0x0; ++ ++ if (nls_uniname_cmp(sb, uniname, entry_uniname)) { ++ is_feasible_entry = FALSE; ++ } else if (order == num_ext_entries) { ++ p_fs->hint_uentry.dir = CLUSTER_32(~0); ++ p_fs->hint_uentry.entry = -1; ++ return(dentry - (num_ext_entries)); ++ } ++ ++ *(uniname+len) = unichar; ++ } ++ } else { ++ is_feasible_entry = FALSE; ++ } ++ } ++ } ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ break; ++ ++ if (clu.flags == 0x03) { ++ if ((--clu.size) > 0) ++ clu.dir++; ++ else ++ clu.dir = CLUSTER_32(~0); ++ } else { ++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) ++ return -2; ++ } ++ } ++ ++ return -2; ++} ++ ++INT32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry) ++{ ++ INT32 count = 0; ++ UINT8 chksum; ++ DOS_DENTRY_T *dos_ep = (DOS_DENTRY_T *) p_entry; ++ EXT_DENTRY_T *ext_ep; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); ++ ++ for (entry--; entry >= 0; entry--) { ++ ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL); ++ if (!ext_ep) ++ return -1; ++ ++ if ((p_fs->fs_func->get_entry_type((DENTRY_T *) ext_ep) == TYPE_EXTEND) && ++ (ext_ep->checksum == chksum)) { ++ count++; ++ if (ext_ep->order > 0x40) ++ return(count); ++ } else { ++ return(count); ++ } ++ } ++ ++ return(count); ++} ++ ++INT32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry) ++{ ++ INT32 i, count = 0; ++ UINT32 type; ++ FILE_DENTRY_T *file_ep = (FILE_DENTRY_T *) p_entry; ++ DENTRY_T *ext_ep; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ for (i = 0, entry++; i < file_ep->num_ext; i++, entry++) { ++ ext_ep = get_entry_in_dir(sb, p_dir, entry, NULL); ++ if (!ext_ep) ++ return -1; ++ ++ type = p_fs->fs_func->get_entry_type(ext_ep); ++ if ((type == TYPE_EXTEND) || (type == TYPE_STREAM)) { ++ count++; ++ } else { ++ return(count); ++ } ++ } ++ ++ return(count); ++} ++ ++INT32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, UINT32 type) ++{ ++ INT32 i, count = 0; ++ INT32 dentries_per_clu; ++ UINT32 entry_type; ++ CHAIN_T clu; ++ DENTRY_T *ep; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ dentries_per_clu = p_fs->dentries_in_root; ++ else ++ dentries_per_clu = p_fs->dentries_per_clu; ++ ++ clu.dir = p_dir->dir; ++ clu.size = p_dir->size; ++ clu.flags = p_dir->flags; ++ ++ while (clu.dir != CLUSTER_32(~0)) { ++ if (p_fs->dev_ejected) ++ break; ++ ++ for (i = 0; i < dentries_per_clu; i++) { ++ ep = get_entry_in_dir(sb, &clu, i, NULL); ++ if (!ep) ++ return -1; ++ ++ entry_type = p_fs->fs_func->get_entry_type(ep); ++ ++ if (entry_type == TYPE_UNUSED) ++ return(count); ++ if (!(type & TYPE_CRITICAL_PRI) && !(type & TYPE_BENIGN_PRI)) ++ continue; ++ ++ if ((type == TYPE_ALL) || (type == entry_type)) ++ count++; ++ } ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ break; ++ ++ if (clu.flags == 0x03) { ++ if ((--clu.size) > 0) ++ clu.dir++; ++ else ++ clu.dir = CLUSTER_32(~0); ++ } else { ++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) ++ return -1; ++ } ++ } ++ ++ return(count); ++} ++ ++BOOL is_dir_empty(struct super_block *sb, CHAIN_T *p_dir) ++{ ++ INT32 i, count = 0; ++ INT32 dentries_per_clu; ++ UINT32 type; ++ CHAIN_T clu; ++ DENTRY_T *ep; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ dentries_per_clu = p_fs->dentries_in_root; ++ else ++ dentries_per_clu = p_fs->dentries_per_clu; ++ ++ clu.dir = p_dir->dir; ++ clu.size = p_dir->size; ++ clu.flags = p_dir->flags; ++ ++ while (clu.dir != CLUSTER_32(~0)) { ++ if (p_fs->dev_ejected) ++ break; ++ ++ for (i = 0; i < dentries_per_clu; i++) { ++ ep = get_entry_in_dir(sb, &clu, i, NULL); ++ if (!ep) ++ break; ++ ++ type = p_fs->fs_func->get_entry_type(ep); ++ ++ if (type == TYPE_UNUSED) ++ return TRUE; ++ if ((type != TYPE_FILE) && (type != TYPE_DIR)) ++ continue; ++ ++ if (p_dir->dir == CLUSTER_32(0)) { ++ return FALSE; ++ } else { ++ if (p_fs->vol_type == EXFAT) ++ return FALSE; ++ if ((p_dir->dir == p_fs->root_dir) || ((++count) > 2)) ++ return FALSE; ++ } ++ } ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ break; ++ ++ if (clu.flags == 0x03) { ++ if ((--clu.size) > 0) ++ clu.dir++; ++ else ++ clu.dir = CLUSTER_32(~0); ++ } else { ++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) ++ break; ++ } ++ } ++ ++ return TRUE; ++} ++ ++INT32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 *entries, DOS_NAME_T *p_dosname) ++{ ++ INT32 ret, num_entries, lossy = FALSE; ++ INT8 **r; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ num_entries = p_fs->fs_func->calc_num_entries(p_uniname); ++ if (num_entries == 0) ++ return FFS_INVALIDPATH; ++ ++ if (p_fs->vol_type != EXFAT) { ++ nls_uniname_to_dosname(sb, p_dosname, p_uniname, &lossy); ++ ++ if (lossy) { ++ ret = fat_generate_dos_name(sb, p_dir, p_dosname); ++ if (ret) ++ return ret; ++ } else { ++ for (r = reserved_names; *r; r++) { ++ if (!STRNCMP((void *) p_dosname->name, *r, 8)) ++ return FFS_INVALIDPATH; ++ } ++ ++ if (p_dosname->name_case != 0xFF) ++ num_entries = 1; ++ } ++ ++ if (num_entries > 1) ++ p_dosname->name_case = 0x0; ++ } ++ ++ *entries = num_entries; ++ ++ return FFS_SUCCESS; ++} ++ ++void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, UINT8 mode) ++{ ++ DOS_NAME_T dos_name; ++ ++ if (mode == 0x0) ++ dos_name.name_case = 0x0; ++ else ++ dos_name.name_case = ep->lcase; ++ ++ MEMCPY(dos_name.name, ep->name, DOS_NAME_LENGTH); ++ nls_dosname_to_uniname(sb, p_uniname, &dos_name); ++} ++ ++void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname) ++{ ++ INT32 i; ++ EXT_DENTRY_T *ep; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ for (entry--, i = 1; entry >= 0; entry--, i++) { ++ ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL); ++ if (!ep) ++ return; ++ ++ if (p_fs->fs_func->get_entry_type((DENTRY_T *) ep) == TYPE_EXTEND) { ++ extract_uni_name_from_ext_entry(ep, uniname, i); ++ if (ep->order > 0x40) ++ return; ++ } else { ++ return; ++ } ++ ++ uniname += 13; ++ } ++} ++ ++void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname) ++{ ++ INT32 i; ++ DENTRY_T *ep; ++ ENTRY_SET_CACHE_T *es; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ es = get_entry_set_in_dir(sb, p_dir, entry, ES_ALL_ENTRIES, &ep); ++ if (es == NULL || es->num_entries < 3) { ++ if(es) { ++ release_entry_set(es); ++ } ++ return; ++ } ++ ++ ep += 2; ++ ++ for (i = 2; i < es->num_entries; i++, ep++) { ++ if (p_fs->fs_func->get_entry_type(ep) == TYPE_EXTEND) { ++ extract_uni_name_from_name_entry((NAME_DENTRY_T *)ep, uniname, i); ++ } else { ++ goto out; ++ } ++ uniname += 15; ++ } ++ ++out: ++ release_entry_set(es); ++} ++ ++INT32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, UINT16 *uniname, INT32 order) ++{ ++ INT32 i, len = 0; ++ ++ for (i = 0; i < 10; i += 2) { ++ *uniname = GET16(ep->unicode_0_4+i); ++ if (*uniname == 0x0) ++ return(len); ++ uniname++; ++ len++; ++ } ++ ++ if (order < 20) { ++ for (i = 0; i < 12; i += 2) { ++ *uniname = GET16_A(ep->unicode_5_10+i); ++ if (*uniname == 0x0) ++ return(len); ++ uniname++; ++ len++; ++ } ++ } else { ++ for (i = 0; i < 8; i += 2) { ++ *uniname = GET16_A(ep->unicode_5_10+i); ++ if (*uniname == 0x0) ++ return(len); ++ uniname++; ++ len++; ++ } ++ *uniname = 0x0; ++ return(len); ++ } ++ ++ for (i = 0; i < 4; i += 2) { ++ *uniname = GET16_A(ep->unicode_11_12+i); ++ if (*uniname == 0x0) ++ return(len); ++ uniname++; ++ len++; ++ } ++ ++ *uniname = 0x0; ++ return(len); ++ ++} ++ ++INT32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname, INT32 order) ++{ ++ INT32 i, len = 0; ++ ++ for (i = 0; i < 30; i += 2) { ++ *uniname = GET16_A(ep->unicode_0_14+i); ++ if (*uniname == 0x0) ++ return(len); ++ uniname++; ++ len++; ++ } ++ ++ *uniname = 0x0; ++ return(len); ++ ++} ++ ++INT32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname) ++{ ++ INT32 i, j, count = 0, count_begin = FALSE; ++ INT32 dentries_per_clu; ++ UINT32 type; ++ UINT8 bmap[128]; ++ CHAIN_T clu; ++ DOS_DENTRY_T *ep; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ Bitmap_clear_all(bmap, 128); ++ Bitmap_set(bmap, 0); ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ dentries_per_clu = p_fs->dentries_in_root; ++ else ++ dentries_per_clu = p_fs->dentries_per_clu; ++ ++ clu.dir = p_dir->dir; ++ clu.flags = p_dir->flags; ++ ++ while (clu.dir != CLUSTER_32(~0)) { ++ if (p_fs->dev_ejected) ++ break; ++ ++ for (i = 0; i < dentries_per_clu; i++) { ++ ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); ++ if (!ep) ++ return FFS_MEDIAERR; ++ ++ type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); ++ ++ if (type == TYPE_UNUSED) ++ break; ++ if ((type != TYPE_FILE) && (type != TYPE_DIR)) ++ continue; ++ ++ count = 0; ++ count_begin = FALSE; ++ ++ for (j = 0; j < 8; j++) { ++ if (ep->name[j] == ' ') ++ break; ++ ++ if (ep->name[j] == '~') { ++ count_begin = TRUE; ++ } else if (count_begin) { ++ if ((ep->name[j] >= '0') && (ep->name[j] <= '9')) { ++ count = count * 10 + (ep->name[j] - '0'); ++ } else { ++ count = 0; ++ count_begin = FALSE; ++ } ++ } ++ } ++ ++ if ((count > 0) && (count < 1024)) ++ Bitmap_set(bmap, count); ++ } ++ ++ if (p_dir->dir == CLUSTER_32(0)) ++ break; ++ ++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) ++ return FFS_MEDIAERR; ++ } ++ ++ count = 0; ++ for (i = 0; i < 128; i++) { ++ if (bmap[i] != 0xFF) { ++ for (j = 0; j < 8; j++) { ++ if (Bitmap_test(&(bmap[i]), j) == 0) { ++ count = (i << 3) + j; ++ break; ++ } ++ } ++ if (count != 0) ++ break; ++ } ++ } ++ ++ if ((count == 0) || (count >= 1024)) ++ return FFS_FILEEXIST; ++ else ++ fat_attach_count_to_dos_name(p_dosname->name, count); ++ ++ return FFS_SUCCESS; ++} ++ ++void fat_attach_count_to_dos_name(UINT8 *dosname, INT32 count) ++{ ++ INT32 i, j, length; ++ INT8 str_count[6]; ++ ++ str_count[0] = '~'; ++ str_count[1] = '\0'; ++ my_itoa(&(str_count[1]), count); ++ length = STRLEN(str_count); ++ ++ i = j = 0; ++ while (j <= (8 - length)) { ++ i = j; ++ if (dosname[j] == ' ') ++ break; ++ if (dosname[j] & 0x80) ++ j += 2; ++ else ++ j++; ++ } ++ ++ for (j = 0; j < length; i++, j++) ++ dosname[i] = (UINT8) str_count[j]; ++ ++ if (i == 7) ++ dosname[7] = ' '; ++ ++} ++ ++INT32 fat_calc_num_entries(UNI_NAME_T *p_uniname) ++{ ++ INT32 len; ++ ++ len = p_uniname->name_len; ++ if (len == 0) ++ return 0; ++ ++ return((len-1) / 13 + 2); ++ ++} ++ ++INT32 exfat_calc_num_entries(UNI_NAME_T *p_uniname) ++{ ++ INT32 len; ++ ++ len = p_uniname->name_len; ++ if (len == 0) ++ return 0; ++ ++ return((len-1) / 15 + 3); ++ ++} ++ ++UINT8 calc_checksum_1byte(void *data, INT32 len, UINT8 chksum) ++{ ++ INT32 i; ++ UINT8 *c = (UINT8 *) data; ++ ++ for (i = 0; i < len; i++, c++) ++ chksum = (((chksum & 1) << 7) | ((chksum & 0xFE) >> 1)) + *c; ++ ++ return(chksum); ++} ++ ++UINT16 calc_checksum_2byte(void *data, INT32 len, UINT16 chksum, INT32 type) ++{ ++ INT32 i; ++ UINT8 *c = (UINT8 *) data; ++ ++ switch (type) { ++ case CS_DIR_ENTRY: ++ for (i = 0; i < len; i++, c++) { ++ if ((i == 2) || (i == 3)) ++ continue; ++ chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (UINT16) *c; ++ } ++ break; ++ default ++ : ++ for (i = 0; i < len; i++, c++) { ++ chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (UINT16) *c; ++ } ++ } ++ ++ return(chksum); ++} ++ ++UINT32 calc_checksum_4byte(void *data, INT32 len, UINT32 chksum, INT32 type) ++{ ++ INT32 i; ++ UINT8 *c = (UINT8 *) data; ++ ++ switch (type) { ++ case CS_PBR_SECTOR: ++ for (i = 0; i < len; i++, c++) { ++ if ((i == 106) || (i == 107) || (i == 112)) ++ continue; ++ chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (UINT32) *c; ++ } ++ break; ++ default ++ : ++ for (i = 0; i < len; i++, c++) { ++ chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (UINT32) *c; ++ } ++ } ++ ++ return(chksum); ++} ++ ++ ++INT32 resolve_path(struct inode *inode, UINT8 *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname) ++{ ++ INT32 lossy = FALSE; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); ++ ++ if (STRLEN(path) >= (MAX_NAME_LENGTH * MAX_CHARSET_SIZE)) ++ return(FFS_INVALIDPATH); ++ ++ STRCPY(name_buf, path); ++ ++ nls_cstring_to_uniname(sb, p_uniname, name_buf, &lossy); ++ if (lossy) ++ return(FFS_INVALIDPATH); ++ ++ fid->size = i_size_read(inode); ++ ++ p_dir->dir = fid->start_clu; ++ p_dir->size = (INT32)(fid->size >> p_fs->cluster_size_bits); ++ p_dir->flags = fid->flags; ++ ++ return(FFS_SUCCESS); ++} ++ ++ ++static FS_FUNC_T fat_fs_func = { ++ .alloc_cluster = fat_alloc_cluster, ++ .free_cluster = fat_free_cluster, ++ .count_used_clusters = fat_count_used_clusters, ++ ++ .init_dir_entry = fat_init_dir_entry, ++ .init_ext_entry = fat_init_ext_entry, ++ .find_dir_entry = fat_find_dir_entry, ++ .delete_dir_entry = fat_delete_dir_entry, ++ .get_uni_name_from_ext_entry = fat_get_uni_name_from_ext_entry, ++ .count_ext_entries = fat_count_ext_entries, ++ .calc_num_entries = fat_calc_num_entries, ++ ++ .get_entry_type = fat_get_entry_type, ++ .set_entry_type = fat_set_entry_type, ++ .get_entry_attr = fat_get_entry_attr, ++ .set_entry_attr = fat_set_entry_attr, ++ .get_entry_flag = fat_get_entry_flag, ++ .set_entry_flag = fat_set_entry_flag, ++ .get_entry_clu0 = fat_get_entry_clu0, ++ .set_entry_clu0 = fat_set_entry_clu0, ++ .get_entry_size = fat_get_entry_size, ++ .set_entry_size = fat_set_entry_size, ++ .get_entry_time = fat_get_entry_time, ++ .set_entry_time = fat_set_entry_time, ++}; ++ ++ ++INT32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) ++{ ++ INT32 num_reserved, num_root_sectors; ++ BPB16_T *p_bpb = (BPB16_T *) p_pbr->bpb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ if (p_bpb->num_fats == 0) ++ return FFS_FORMATERR; ++ ++ num_root_sectors = GET16(p_bpb->num_root_entries) << DENTRY_SIZE_BITS; ++ num_root_sectors = ((num_root_sectors-1) >> p_bd->sector_size_bits) + 1; ++ ++ p_fs->sectors_per_clu = p_bpb->sectors_per_clu; ++ p_fs->sectors_per_clu_bits = my_log2(p_bpb->sectors_per_clu); ++ p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; ++ p_fs->cluster_size = 1 << p_fs->cluster_size_bits; ++ ++ p_fs->num_FAT_sectors = GET16(p_bpb->num_fat_sectors); ++ ++ p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved); ++ if (p_bpb->num_fats == 1) ++ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; ++ else ++ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; ++ ++ p_fs->root_start_sector = p_fs->FAT2_start_sector + p_fs->num_FAT_sectors; ++ p_fs->data_start_sector = p_fs->root_start_sector + num_root_sectors; ++ ++ p_fs->num_sectors = GET16(p_bpb->num_sectors); ++ if (p_fs->num_sectors == 0) ++ p_fs->num_sectors = GET32(p_bpb->num_huge_sectors); ++ ++ num_reserved = p_fs->data_start_sector - p_fs->PBR_sector; ++ p_fs->num_clusters = ((p_fs->num_sectors - num_reserved) >> p_fs->sectors_per_clu_bits) + 2; ++ ++ if (p_fs->num_clusters < FAT12_THRESHOLD) ++ p_fs->vol_type = FAT12; ++ else ++ p_fs->vol_type = FAT16; ++ p_fs->vol_id = GET32(p_bpb->vol_serial); ++ ++ p_fs->root_dir = 0; ++ p_fs->dentries_in_root = GET16(p_bpb->num_root_entries); ++ p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); ++ ++ p_fs->vol_flag = VOL_CLEAN; ++ p_fs->clu_srch_ptr = 2; ++ p_fs->used_clusters = (UINT32) ~0; ++ ++ p_fs->fs_func = &fat_fs_func; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) ++{ ++ INT32 num_reserved; ++ BPB32_T *p_bpb = (BPB32_T *) p_pbr->bpb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ if (p_bpb->num_fats == 0) ++ return FFS_FORMATERR; ++ ++ p_fs->sectors_per_clu = p_bpb->sectors_per_clu; ++ p_fs->sectors_per_clu_bits = my_log2(p_bpb->sectors_per_clu); ++ p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; ++ p_fs->cluster_size = 1 << p_fs->cluster_size_bits; ++ ++ p_fs->num_FAT_sectors = GET32(p_bpb->num_fat32_sectors); ++ ++ p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved); ++ if (p_bpb->num_fats == 1) ++ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; ++ else ++ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; ++ ++ p_fs->root_start_sector = p_fs->FAT2_start_sector + p_fs->num_FAT_sectors; ++ p_fs->data_start_sector = p_fs->root_start_sector; ++ ++ p_fs->num_sectors = GET32(p_bpb->num_huge_sectors); ++ num_reserved = p_fs->data_start_sector - p_fs->PBR_sector; ++ ++ p_fs->num_clusters = ((p_fs->num_sectors-num_reserved) >> p_fs->sectors_per_clu_bits) + 2; ++ ++ p_fs->vol_type = FAT32; ++ p_fs->vol_id = GET32(p_bpb->vol_serial); ++ ++ p_fs->root_dir = GET32(p_bpb->root_cluster); ++ p_fs->dentries_in_root = 0; ++ p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); ++ ++ p_fs->vol_flag = VOL_CLEAN; ++ p_fs->clu_srch_ptr = 2; ++ p_fs->used_clusters = (UINT32) ~0; ++ ++ p_fs->fs_func = &fat_fs_func; ++ ++ return FFS_SUCCESS; ++} ++ ++static FS_FUNC_T exfat_fs_func = { ++ .alloc_cluster = exfat_alloc_cluster, ++ .free_cluster = exfat_free_cluster, ++ .count_used_clusters = exfat_count_used_clusters, ++ ++ .init_dir_entry = exfat_init_dir_entry, ++ .init_ext_entry = exfat_init_ext_entry, ++ .find_dir_entry = exfat_find_dir_entry, ++ .delete_dir_entry = exfat_delete_dir_entry, ++ .get_uni_name_from_ext_entry = exfat_get_uni_name_from_ext_entry, ++ .count_ext_entries = exfat_count_ext_entries, ++ .calc_num_entries = exfat_calc_num_entries, ++ ++ .get_entry_type = exfat_get_entry_type, ++ .set_entry_type = exfat_set_entry_type, ++ .get_entry_attr = exfat_get_entry_attr, ++ .set_entry_attr = exfat_set_entry_attr, ++ .get_entry_flag = exfat_get_entry_flag, ++ .set_entry_flag = exfat_set_entry_flag, ++ .get_entry_clu0 = exfat_get_entry_clu0, ++ .set_entry_clu0 = exfat_set_entry_clu0, ++ .get_entry_size = exfat_get_entry_size, ++ .set_entry_size = exfat_set_entry_size, ++ .get_entry_time = exfat_get_entry_time, ++ .set_entry_time = exfat_set_entry_time, ++}; ++ ++INT32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) ++{ ++ BPBEX_T *p_bpb = (BPBEX_T *) p_pbr->bpb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ if (p_bpb->num_fats == 0) ++ return FFS_FORMATERR; ++ ++ p_fs->sectors_per_clu = 1 << p_bpb->sectors_per_clu_bits; ++ p_fs->sectors_per_clu_bits = p_bpb->sectors_per_clu_bits; ++ p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; ++ p_fs->cluster_size = 1 << p_fs->cluster_size_bits; ++ ++ p_fs->num_FAT_sectors = GET32(p_bpb->fat_length); ++ ++ p_fs->FAT1_start_sector = p_fs->PBR_sector + GET32(p_bpb->fat_offset); ++ if (p_bpb->num_fats == 1) ++ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; ++ else ++ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; ++ ++ p_fs->root_start_sector = p_fs->PBR_sector + GET32(p_bpb->clu_offset); ++ p_fs->data_start_sector = p_fs->root_start_sector; ++ ++ p_fs->num_sectors = GET64(p_bpb->vol_length); ++ p_fs->num_clusters = GET32(p_bpb->clu_count) + 2; ++ ++ p_fs->vol_type = EXFAT; ++ p_fs->vol_id = GET32(p_bpb->vol_serial); ++ ++ p_fs->root_dir = GET32(p_bpb->root_cluster); ++ p_fs->dentries_in_root = 0; ++ p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); ++ ++ p_fs->vol_flag = (UINT32) GET16(p_bpb->vol_flags); ++ p_fs->clu_srch_ptr = 2; ++ p_fs->used_clusters = (UINT32) ~0; ++ ++ p_fs->fs_func = &exfat_fs_func; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) ++{ ++ INT32 ret, dentry, num_entries; ++ UINT64 size; ++ CHAIN_T clu; ++ DOS_NAME_T dos_name, dot_name; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name); ++ if (ret) ++ return ret; ++ ++ dentry = find_empty_entry(inode, p_dir, num_entries); ++ if (dentry < 0) ++ return FFS_FULL; ++ ++ clu.dir = CLUSTER_32(~0); ++ clu.size = 0; ++ clu.flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; ++ ++ ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu); ++ if (ret < 1) ++ return FFS_FULL; ++ ++ ret = clear_cluster(sb, clu.dir); ++ if (ret != FFS_SUCCESS) ++ return ret; ++ ++ if (p_fs->vol_type == EXFAT) { ++ size = p_fs->cluster_size; ++ } else { ++ size = 0; ++ ++ dot_name.name_case = 0x0; ++ MEMCPY(dot_name.name, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH); ++ ++ ret = p_fs->fs_func->init_dir_entry(sb, &clu, 0, TYPE_DIR, clu.dir, 0); ++ if (ret != FFS_SUCCESS) ++ return ret; ++ ++ ret = p_fs->fs_func->init_ext_entry(sb, &clu, 0, 1, NULL, &dot_name); ++ if (ret != FFS_SUCCESS) ++ return ret; ++ ++ MEMCPY(dot_name.name, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH); ++ ++ if (p_dir->dir == p_fs->root_dir) ++ ret = p_fs->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, CLUSTER_32(0), 0); ++ else ++ ret = p_fs->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, p_dir->dir, 0); ++ ++ if (ret != FFS_SUCCESS) ++ return ret; ++ ++ ret = p_fs->fs_func->init_ext_entry(sb, &clu, 1, 1, NULL, &dot_name); ++ if (ret != FFS_SUCCESS) ++ return ret; ++ } ++ ++ ret = p_fs->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_DIR, clu.dir, size); ++ if (ret != FFS_SUCCESS) ++ return ret; ++ ++ ret = p_fs->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); ++ if (ret != FFS_SUCCESS) ++ return ret; ++ ++ fid->dir.dir = p_dir->dir; ++ fid->dir.size = p_dir->size; ++ fid->dir.flags = p_dir->flags; ++ fid->entry = dentry; ++ ++ fid->attr = ATTR_SUBDIR; ++ fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; ++ fid->size = size; ++ fid->start_clu = clu.dir; ++ ++ fid->type= TYPE_DIR; ++ fid->rwoffset = 0; ++ fid->hint_last_off = -1; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, UINT8 mode, FILE_ID_T *fid) ++{ ++ INT32 ret, dentry, num_entries; ++ DOS_NAME_T dos_name; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name); ++ if (ret) ++ return ret; ++ ++ dentry = find_empty_entry(inode, p_dir, num_entries); ++ if (dentry < 0) ++ return FFS_FULL; ++ ++ ret = p_fs->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_FILE | mode, CLUSTER_32(0), 0); ++ if (ret != FFS_SUCCESS) ++ return ret; ++ ++ ret = p_fs->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); ++ if (ret != FFS_SUCCESS) ++ return ret; ++ ++ fid->dir.dir = p_dir->dir; ++ fid->dir.size = p_dir->size; ++ fid->dir.flags = p_dir->flags; ++ fid->entry = dentry; ++ ++ fid->attr = ATTR_ARCHIVE | mode; ++ fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; ++ fid->size = 0; ++ fid->start_clu = CLUSTER_32(~0); ++ ++ fid->type= TYPE_FILE; ++ fid->rwoffset = 0; ++ fid->hint_last_off = -1; ++ ++ return FFS_SUCCESS; ++} ++ ++void remove_file(struct inode *inode, CHAIN_T *p_dir, INT32 entry) ++{ ++ INT32 num_entries; ++ UINT32 sector; ++ DENTRY_T *ep; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ ep = get_entry_in_dir(sb, p_dir, entry, §or); ++ if (!ep) ++ return; ++ ++ buf_lock(sb, sector); ++ ++ num_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, entry, ep); ++ if (num_entries < 0) { ++ buf_unlock(sb, sector); ++ return; ++ } ++ num_entries++; ++ ++ buf_unlock(sb, sector); ++ ++ p_fs->fs_func->delete_dir_entry(sb, p_dir, entry, 0, num_entries); ++} ++ ++INT32 rename_file(struct inode *inode, CHAIN_T *p_dir, INT32 oldentry, UNI_NAME_T *p_uniname, FILE_ID_T *fid) ++{ ++ INT32 ret, newentry = -1, num_old_entries, num_new_entries; ++ UINT32 sector_old, sector_new; ++ DOS_NAME_T dos_name; ++ DENTRY_T *epold, *epnew; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ epold = get_entry_in_dir(sb, p_dir, oldentry, §or_old); ++ if (!epold) ++ return FFS_MEDIAERR; ++ ++ buf_lock(sb, sector_old); ++ ++ num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, oldentry, epold); ++ if (num_old_entries < 0) { ++ buf_unlock(sb, sector_old); ++ return FFS_MEDIAERR; ++ } ++ num_old_entries++; ++ ++ ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_new_entries, &dos_name); ++ if (ret) { ++ buf_unlock(sb, sector_old); ++ return ret; ++ } ++ ++ if (num_old_entries < num_new_entries) { ++ newentry = find_empty_entry(inode, p_dir, num_new_entries); ++ if (newentry < 0) { ++ buf_unlock(sb, sector_old); ++ return FFS_FULL; ++ } ++ ++ epnew = get_entry_in_dir(sb, p_dir, newentry, §or_new); ++ if (!epnew) { ++ buf_unlock(sb, sector_old); ++ return FFS_MEDIAERR; ++ } ++ ++ MEMCPY((void *) epnew, (void *) epold, DENTRY_SIZE); ++ if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) { ++ p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); ++ fid->attr |= ATTR_ARCHIVE; ++ } ++ buf_modify(sb, sector_new); ++ buf_unlock(sb, sector_old); ++ ++ if (p_fs->vol_type == EXFAT) { ++ epold = get_entry_in_dir(sb, p_dir, oldentry+1, §or_old); ++ buf_lock(sb, sector_old); ++ epnew = get_entry_in_dir(sb, p_dir, newentry+1, §or_new); ++ ++ if (!epold || !epnew) { ++ buf_unlock(sb, sector_old); ++ return FFS_MEDIAERR; ++ } ++ ++ MEMCPY((void *) epnew, (void *) epold, DENTRY_SIZE); ++ buf_modify(sb, sector_new); ++ buf_unlock(sb, sector_old); ++ } ++ ++ ret = p_fs->fs_func->init_ext_entry(sb, p_dir, newentry, num_new_entries, p_uniname, &dos_name); ++ if (ret != FFS_SUCCESS) ++ return ret; ++ ++ p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, 0, num_old_entries); ++ fid->entry = newentry; ++ } else { ++ if (p_fs->fs_func->get_entry_type(epold) == TYPE_FILE) { ++ p_fs->fs_func->set_entry_attr(epold, p_fs->fs_func->get_entry_attr(epold) | ATTR_ARCHIVE); ++ fid->attr |= ATTR_ARCHIVE; ++ } ++ buf_modify(sb, sector_old); ++ buf_unlock(sb, sector_old); ++ ++ ret = p_fs->fs_func->init_ext_entry(sb, p_dir, oldentry, num_new_entries, p_uniname, &dos_name); ++ if (ret != FFS_SUCCESS) ++ return ret; ++ ++ p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, num_new_entries, num_old_entries); ++ } ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 move_file(struct inode *inode, CHAIN_T *p_olddir, INT32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) ++{ ++ INT32 ret, newentry, num_new_entries, num_old_entries; ++ UINT32 sector_mov, sector_new; ++ CHAIN_T clu; ++ DOS_NAME_T dos_name; ++ DENTRY_T *epmov, *epnew; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ epmov = get_entry_in_dir(sb, p_olddir, oldentry, §or_mov); ++ if (!epmov) ++ return FFS_MEDIAERR; ++ ++ if (p_fs->fs_func->get_entry_type(epmov) == TYPE_DIR && ++ p_fs->fs_func->get_entry_clu0(epmov) == p_newdir->dir) ++ return FFS_INVALIDPATH; ++ ++ buf_lock(sb, sector_mov); ++ ++ num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_olddir, oldentry, epmov); ++ if (num_old_entries < 0) { ++ buf_unlock(sb, sector_mov); ++ return FFS_MEDIAERR; ++ } ++ num_old_entries++; ++ ++ ret = get_num_entries_and_dos_name(sb, p_newdir, p_uniname, &num_new_entries, &dos_name); ++ if (ret) { ++ buf_unlock(sb, sector_mov); ++ return ret; ++ } ++ ++ newentry = find_empty_entry(inode, p_newdir, num_new_entries); ++ if (newentry < 0) { ++ buf_unlock(sb, sector_mov); ++ return FFS_FULL; ++ } ++ ++ epnew = get_entry_in_dir(sb, p_newdir, newentry, §or_new); ++ if (!epnew) { ++ buf_unlock(sb, sector_mov); ++ return FFS_MEDIAERR; ++ } ++ ++ MEMCPY((void *) epnew, (void *) epmov, DENTRY_SIZE); ++ if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) { ++ p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); ++ fid->attr |= ATTR_ARCHIVE; ++ } ++ buf_modify(sb, sector_new); ++ buf_unlock(sb, sector_mov); ++ ++ if (p_fs->vol_type == EXFAT) { ++ epmov = get_entry_in_dir(sb, p_olddir, oldentry+1, §or_mov); ++ buf_lock(sb, sector_mov); ++ epnew = get_entry_in_dir(sb, p_newdir, newentry+1, §or_new); ++ if (!epmov || !epnew) { ++ buf_unlock(sb, sector_mov); ++ return FFS_MEDIAERR; ++ } ++ ++ MEMCPY((void *) epnew, (void *) epmov, DENTRY_SIZE); ++ buf_modify(sb, sector_new); ++ buf_unlock(sb, sector_mov); ++ } else if (p_fs->fs_func->get_entry_type(epnew) == TYPE_DIR) { ++ clu.dir = p_fs->fs_func->get_entry_clu0(epnew); ++ clu.flags = 0x01; ++ ++ epnew = get_entry_in_dir(sb, &clu, 1, §or_new); ++ if (!epnew) ++ return FFS_MEDIAERR; ++ ++ if (p_newdir->dir == p_fs->root_dir) ++ p_fs->fs_func->set_entry_clu0(epnew, CLUSTER_32(0)); ++ else ++ p_fs->fs_func->set_entry_clu0(epnew, p_newdir->dir); ++ buf_modify(sb, sector_new); ++ } ++ ++ ret = p_fs->fs_func->init_ext_entry(sb, p_newdir, newentry, num_new_entries, p_uniname, &dos_name); ++ if (ret != FFS_SUCCESS) ++ return ret; ++ ++ p_fs->fs_func->delete_dir_entry(sb, p_olddir, oldentry, 0, num_old_entries); ++ ++ fid->dir.dir = p_newdir->dir; ++ fid->dir.size = p_newdir->size; ++ fid->dir.flags = p_newdir->flags; ++ ++ fid->entry = newentry; ++ ++ return FFS_SUCCESS; ++} ++ ++INT32 sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 read) ++{ ++ INT32 ret = FFS_MEDIAERR; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if ((sec >= (p_fs->PBR_sector+p_fs->num_sectors)) && (p_fs->num_sectors > 0)) { ++ PRINT("[EXFAT] sector_read: out of range error! (sec = %d)\n", sec); ++ fs_error(sb); ++ return ret; ++ } ++ ++ if (!p_fs->dev_ejected) { ++ ret = bdev_read(sb, sec, bh, 1, read); ++ if (ret != FFS_SUCCESS) ++ p_fs->dev_ejected = TRUE; ++ } ++ ++ return ret; ++} ++ ++INT32 sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 sync) ++{ ++ INT32 ret = FFS_MEDIAERR; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (sec >= (p_fs->PBR_sector+p_fs->num_sectors) && (p_fs->num_sectors > 0)) { ++ PRINT("[EXFAT] sector_write: out of range error! (sec = %d)\n", sec); ++ fs_error(sb); ++ return ret; ++ } ++ ++ if (bh == NULL) { ++ PRINT("[EXFAT] sector_write: bh is NULL!\n"); ++ fs_error(sb); ++ return ret; ++ } ++ ++ if (!p_fs->dev_ejected) { ++ ret = bdev_write(sb, sec, bh, 1, sync); ++ if (ret != FFS_SUCCESS) ++ p_fs->dev_ejected = TRUE; ++ } ++ ++ return ret; ++} ++ ++INT32 multi_sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 num_secs, INT32 read) ++{ ++ INT32 ret = FFS_MEDIAERR; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (((sec+num_secs) > (p_fs->PBR_sector+p_fs->num_sectors)) && (p_fs->num_sectors > 0)) { ++ PRINT("[EXFAT] multi_sector_read: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs); ++ fs_error(sb); ++ return ret; ++ } ++ ++ if (!p_fs->dev_ejected) { ++ ret = bdev_read(sb, sec, bh, num_secs, read); ++ if (ret != FFS_SUCCESS) ++ p_fs->dev_ejected = TRUE; ++ } ++ ++ return ret; ++} ++ ++INT32 multi_sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 num_secs, INT32 sync) ++{ ++ INT32 ret = FFS_MEDIAERR; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if ((sec+num_secs) > (p_fs->PBR_sector+p_fs->num_sectors) && (p_fs->num_sectors > 0)) { ++ PRINT("[EXFAT] multi_sector_write: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs); ++ fs_error(sb); ++ return ret; ++ } ++ if (bh == NULL) { ++ PRINT("[EXFAT] multi_sector_write: bh is NULL!\n"); ++ fs_error(sb); ++ return ret; ++ } ++ ++ if (!p_fs->dev_ejected) { ++ ret = bdev_write(sb, sec, bh, num_secs, sync); ++ if (ret != FFS_SUCCESS) ++ p_fs->dev_ejected = TRUE; ++ } ++ ++ return ret; ++} +diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h +new file mode 100644 +index 00000000..a10f4894 +--- /dev/null ++++ b/drivers/staging/exfat/exfat.h +@@ -0,0 +1,598 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _EXFAT_H ++#define _EXFAT_H ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++#include "exfat_data.h" ++#include "exfat_oal.h" ++ ++#include "exfat_blkdev.h" ++#include "exfat_cache.h" ++#include "exfat_nls.h" ++#include "exfat_api.h" ++#include "exfat_cache.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#if EXFAT_CONFIG_KERNEL_DEBUG ++#define EXFAT_IOC_GET_DEBUGFLAGS _IOR('f', 100, long) ++#define EXFAT_IOC_SET_DEBUGFLAGS _IOW('f', 101, long) ++ ++#define EXFAT_DEBUGFLAGS_INVALID_UMOUNT 0x01 ++#define EXFAT_DEBUGFLAGS_ERROR_RW 0x02 ++#endif ++ ++#define MAX_VOLUME 4 ++ ++#define DENTRY_SIZE 32 ++#define DENTRY_SIZE_BITS 5 ++ ++#define PBR_SIGNATURE 0xAA55 ++#define EXT_SIGNATURE 0xAA550000 ++#define VOL_LABEL "NO NAME " ++#define OEM_NAME "MSWIN4.1" ++#define STR_FAT12 "FAT12 " ++#define STR_FAT16 "FAT16 " ++#define STR_FAT32 "FAT32 " ++#define STR_EXFAT "EXFAT " ++#define VOL_CLEAN 0x0000 ++#define VOL_DIRTY 0x0002 ++ ++#define FAT12_THRESHOLD 4087 ++#define FAT16_THRESHOLD 65527 ++#define FAT32_THRESHOLD 268435457 ++#define EXFAT_THRESHOLD 268435457 ++ ++#define TYPE_UNUSED 0x0000 ++#define TYPE_DELETED 0x0001 ++#define TYPE_INVALID 0x0002 ++#define TYPE_CRITICAL_PRI 0x0100 ++#define TYPE_BITMAP 0x0101 ++#define TYPE_UPCASE 0x0102 ++#define TYPE_VOLUME 0x0103 ++#define TYPE_DIR 0x0104 ++#define TYPE_FILE 0x011F ++#define TYPE_SYMLINK 0x015F ++#define TYPE_CRITICAL_SEC 0x0200 ++#define TYPE_STREAM 0x0201 ++#define TYPE_EXTEND 0x0202 ++#define TYPE_ACL 0x0203 ++#define TYPE_BENIGN_PRI 0x0400 ++#define TYPE_GUID 0x0401 ++#define TYPE_PADDING 0x0402 ++#define TYPE_ACLTAB 0x0403 ++#define TYPE_BENIGN_SEC 0x0800 ++#define TYPE_ALL 0x0FFF ++ ++#define TM_CREATE 0 ++#define TM_MODIFY 1 ++#define TM_ACCESS 2 ++ ++#define CS_DIR_ENTRY 0 ++#define CS_PBR_SECTOR 1 ++#define CS_DEFAULT 2 ++ ++#define CLUSTER_16(x) ((UINT16)(x)) ++#define CLUSTER_32(x) ((UINT32)(x)) ++ ++#define START_SECTOR(x) \ ++ ( (((x)-2) << p_fs->sectors_per_clu_bits) + p_fs->data_start_sector ) ++ ++#define IS_LAST_SECTOR_IN_CLUSTER(sec) \ ++ ( (((sec) - p_fs->data_start_sector + 1) & ((1 << p_fs->sectors_per_clu_bits) -1)) == 0) ++ ++#define GET_CLUSTER_FROM_SECTOR(sec) \ ++ ((((sec) - p_fs->data_start_sector) >> p_fs->sectors_per_clu_bits) +2) ++ ++#define GET16(p_src) \ ++ ( ((UINT16)(p_src)[0]) | (((UINT16)(p_src)[1]) << 8) ) ++#define GET32(p_src) \ ++ ( ((UINT32)(p_src)[0]) | (((UINT32)(p_src)[1]) << 8) | \ ++ (((UINT32)(p_src)[2]) << 16) | (((UINT32)(p_src)[3]) << 24) ) ++#define GET64(p_src) \ ++ ( ((UINT64)(p_src)[0]) | (((UINT64)(p_src)[1]) << 8) | \ ++ (((UINT64)(p_src)[2]) << 16) | (((UINT64)(p_src)[3]) << 24) | \ ++ (((UINT64)(p_src)[4]) << 32) | (((UINT64)(p_src)[5]) << 40) | \ ++ (((UINT64)(p_src)[6]) << 48) | (((UINT64)(p_src)[7]) << 56) ) ++ ++ ++#define SET16(p_dst,src) \ ++ do { \ ++ (p_dst)[0]=(UINT8)(src); \ ++ (p_dst)[1]=(UINT8)(((UINT16)(src)) >> 8); \ ++ } while (0) ++#define SET32(p_dst,src) \ ++ do { \ ++ (p_dst)[0]=(UINT8)(src); \ ++ (p_dst)[1]=(UINT8)(((UINT32)(src)) >> 8); \ ++ (p_dst)[2]=(UINT8)(((UINT32)(src)) >> 16); \ ++ (p_dst)[3]=(UINT8)(((UINT32)(src)) >> 24); \ ++ } while (0) ++#define SET64(p_dst,src) \ ++ do { \ ++ (p_dst)[0]=(UINT8)(src); \ ++ (p_dst)[1]=(UINT8)(((UINT64)(src)) >> 8); \ ++ (p_dst)[2]=(UINT8)(((UINT64)(src)) >> 16); \ ++ (p_dst)[3]=(UINT8)(((UINT64)(src)) >> 24); \ ++ (p_dst)[4]=(UINT8)(((UINT64)(src)) >> 32); \ ++ (p_dst)[5]=(UINT8)(((UINT64)(src)) >> 40); \ ++ (p_dst)[6]=(UINT8)(((UINT64)(src)) >> 48); \ ++ (p_dst)[7]=(UINT8)(((UINT64)(src)) >> 56); \ ++ } while (0) ++ ++#if (FFS_CONFIG_LITTLE_ENDIAN == 1) ++#define GET16_A(p_src) (*((UINT16 *)(p_src))) ++#define GET32_A(p_src) (*((UINT32 *)(p_src))) ++#define GET64_A(p_src) (*((UINT64 *)(p_src))) ++#define SET16_A(p_dst,src) *((UINT16 *)(p_dst)) = (UINT16)(src) ++#define SET32_A(p_dst,src) *((UINT32 *)(p_dst)) = (UINT32)(src) ++#define SET64_A(p_dst,src) *((UINT64 *)(p_dst)) = (UINT64)(src) ++#else ++#define GET16_A(p_src) GET16(p_src) ++#define GET32_A(p_src) GET32(p_src) ++#define GET64_A(p_src) GET64(p_src) ++#define SET16_A(p_dst,src) SET16(p_dst, src) ++#define SET32_A(p_dst,src) SET32(p_dst, src) ++#define SET64_A(p_dst,src) SET64(p_dst, src) ++#endif ++ ++#define HIGH_INDEX_BIT (8) ++#define HIGH_INDEX_MASK (0xFF00) ++#define LOW_INDEX_BIT (16-HIGH_INDEX_BIT) ++#define UTBL_ROW_COUNT (1<<LOW_INDEX_BIT) ++#define UTBL_COL_COUNT (1<<HIGH_INDEX_BIT) ++ ++ static inline UINT16 get_col_index(UINT16 i) ++ { ++ return i >> LOW_INDEX_BIT; ++ } ++ static inline UINT16 get_row_index(UINT16 i) ++ { ++ return i & ~HIGH_INDEX_MASK; ++ } ++ ++ typedef struct { ++ UINT8 jmp_boot[3]; ++ UINT8 oem_name[8]; ++ UINT8 bpb[109]; ++ UINT8 boot_code[390]; ++ UINT8 signature[2]; ++ } PBR_SECTOR_T; ++ ++ typedef struct { ++ UINT8 sector_size[2]; ++ UINT8 sectors_per_clu; ++ UINT8 num_reserved[2]; ++ UINT8 num_fats; ++ UINT8 num_root_entries[2]; ++ UINT8 num_sectors[2]; ++ UINT8 media_type; ++ UINT8 num_fat_sectors[2]; ++ UINT8 sectors_in_track[2]; ++ UINT8 num_heads[2]; ++ UINT8 num_hid_sectors[4]; ++ UINT8 num_huge_sectors[4]; ++ ++ UINT8 phy_drv_no; ++ UINT8 reserved; ++ UINT8 ext_signature; ++ UINT8 vol_serial[4]; ++ UINT8 vol_label[11]; ++ UINT8 vol_type[8]; ++ } BPB16_T; ++ ++ typedef struct { ++ UINT8 sector_size[2]; ++ UINT8 sectors_per_clu; ++ UINT8 num_reserved[2]; ++ UINT8 num_fats; ++ UINT8 num_root_entries[2]; ++ UINT8 num_sectors[2]; ++ UINT8 media_type; ++ UINT8 num_fat_sectors[2]; ++ UINT8 sectors_in_track[2]; ++ UINT8 num_heads[2]; ++ UINT8 num_hid_sectors[4]; ++ UINT8 num_huge_sectors[4]; ++ UINT8 num_fat32_sectors[4]; ++ UINT8 ext_flags[2]; ++ UINT8 fs_version[2]; ++ UINT8 root_cluster[4]; ++ UINT8 fsinfo_sector[2]; ++ UINT8 backup_sector[2]; ++ UINT8 reserved[12]; ++ ++ UINT8 phy_drv_no; ++ UINT8 ext_reserved; ++ UINT8 ext_signature; ++ UINT8 vol_serial[4]; ++ UINT8 vol_label[11]; ++ UINT8 vol_type[8]; ++ } BPB32_T; ++ ++ typedef struct { ++ UINT8 reserved1[53]; ++ UINT8 vol_offset[8]; ++ UINT8 vol_length[8]; ++ UINT8 fat_offset[4]; ++ UINT8 fat_length[4]; ++ UINT8 clu_offset[4]; ++ UINT8 clu_count[4]; ++ UINT8 root_cluster[4]; ++ UINT8 vol_serial[4]; ++ UINT8 fs_version[2]; ++ UINT8 vol_flags[2]; ++ UINT8 sector_size_bits; ++ UINT8 sectors_per_clu_bits; ++ UINT8 num_fats; ++ UINT8 phy_drv_no; ++ UINT8 perc_in_use; ++ UINT8 reserved2[7]; ++ } BPBEX_T; ++ ++ typedef struct { ++ UINT8 signature1[4]; ++ UINT8 reserved1[480]; ++ UINT8 signature2[4]; ++ UINT8 free_cluster[4]; ++ UINT8 next_cluster[4]; ++ UINT8 reserved2[14]; ++ UINT8 signature3[2]; ++ } FSI_SECTOR_T; ++ ++ typedef struct { ++ UINT8 dummy[32]; ++ } DENTRY_T; ++ ++ typedef struct { ++ UINT8 name[DOS_NAME_LENGTH]; ++ UINT8 attr; ++ UINT8 lcase; ++ UINT8 create_time_ms; ++ UINT8 create_time[2]; ++ UINT8 create_date[2]; ++ UINT8 access_date[2]; ++ UINT8 start_clu_hi[2]; ++ UINT8 modify_time[2]; ++ UINT8 modify_date[2]; ++ UINT8 start_clu_lo[2]; ++ UINT8 size[4]; ++ } DOS_DENTRY_T; ++ ++ typedef struct { ++ UINT8 order; ++ UINT8 unicode_0_4[10]; ++ UINT8 attr; ++ UINT8 sysid; ++ UINT8 checksum; ++ UINT8 unicode_5_10[12]; ++ UINT8 start_clu[2]; ++ UINT8 unicode_11_12[4]; ++ } EXT_DENTRY_T; ++ ++ typedef struct { ++ UINT8 type; ++ UINT8 num_ext; ++ UINT8 checksum[2]; ++ UINT8 attr[2]; ++ UINT8 reserved1[2]; ++ UINT8 create_time[2]; ++ UINT8 create_date[2]; ++ UINT8 modify_time[2]; ++ UINT8 modify_date[2]; ++ UINT8 access_time[2]; ++ UINT8 access_date[2]; ++ UINT8 create_time_ms; ++ UINT8 modify_time_ms; ++ UINT8 access_time_ms; ++ UINT8 reserved2[9]; ++ } FILE_DENTRY_T; ++ ++ typedef struct { ++ UINT8 type; ++ UINT8 flags; ++ UINT8 reserved1; ++ UINT8 name_len; ++ UINT8 name_hash[2]; ++ UINT8 reserved2[2]; ++ UINT8 valid_size[8]; ++ UINT8 reserved3[4]; ++ UINT8 start_clu[4]; ++ UINT8 size[8]; ++ } STRM_DENTRY_T; ++ ++ typedef struct { ++ UINT8 type; ++ UINT8 flags; ++ UINT8 unicode_0_14[30]; ++ } NAME_DENTRY_T; ++ ++ typedef struct { ++ UINT8 type; ++ UINT8 flags; ++ UINT8 reserved[18]; ++ UINT8 start_clu[4]; ++ UINT8 size[8]; ++ } BMAP_DENTRY_T; ++ ++ typedef struct { ++ UINT8 type; ++ UINT8 reserved1[3]; ++ UINT8 checksum[4]; ++ UINT8 reserved2[12]; ++ UINT8 start_clu[4]; ++ UINT8 size[8]; ++ } CASE_DENTRY_T; ++ ++ typedef struct { ++ UINT8 type; ++ UINT8 label_len; ++ UINT8 unicode_0_10[22]; ++ UINT8 reserved[8]; ++ } VOLM_DENTRY_T; ++ ++ typedef struct { ++ UINT32 dir; ++ INT32 entry; ++ CHAIN_T clu; ++ } UENTRY_T; ++ ++ typedef struct __FS_STRUCT_T { ++ UINT32 mounted; ++ struct super_block *sb; ++ struct semaphore v_sem; ++ } FS_STRUCT_T; ++ ++ typedef struct { ++ INT32 (*alloc_cluster)(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain); ++ void (*free_cluster)(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse); ++ INT32 (*count_used_clusters)(struct super_block *sb); ++ ++ INT32 (*init_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, ++ UINT32 start_clu, UINT64 size); ++ INT32 (*init_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, ++ UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); ++ INT32 (*find_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type); ++ void (*delete_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 offset, INT32 num_entries); ++ void (*get_uni_name_from_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname); ++ INT32 (*count_ext_entries)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry); ++ INT32 (*calc_num_entries)(UNI_NAME_T *p_uniname); ++ ++ UINT32 (*get_entry_type)(DENTRY_T *p_entry); ++ void (*set_entry_type)(DENTRY_T *p_entry, UINT32 type); ++ UINT32 (*get_entry_attr)(DENTRY_T *p_entry); ++ void (*set_entry_attr)(DENTRY_T *p_entry, UINT32 attr); ++ UINT8 (*get_entry_flag)(DENTRY_T *p_entry); ++ void (*set_entry_flag)(DENTRY_T *p_entry, UINT8 flag); ++ UINT32 (*get_entry_clu0)(DENTRY_T *p_entry); ++ void (*set_entry_clu0)(DENTRY_T *p_entry, UINT32 clu0); ++ UINT64 (*get_entry_size)(DENTRY_T *p_entry); ++ void (*set_entry_size)(DENTRY_T *p_entry, UINT64 size); ++ void (*get_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); ++ void (*set_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); ++ } FS_FUNC_T; ++ ++ typedef struct __FS_INFO_T { ++ UINT32 drv; ++ UINT32 vol_type; ++ UINT32 vol_id; ++ ++ UINT32 num_sectors; ++ UINT32 num_clusters; ++ UINT32 cluster_size; ++ UINT32 cluster_size_bits; ++ UINT32 sectors_per_clu; ++ UINT32 sectors_per_clu_bits; ++ ++ UINT32 PBR_sector; ++ UINT32 FAT1_start_sector; ++ UINT32 FAT2_start_sector; ++ UINT32 root_start_sector; ++ UINT32 data_start_sector; ++ UINT32 num_FAT_sectors; ++ ++ UINT32 root_dir; ++ UINT32 dentries_in_root; ++ UINT32 dentries_per_clu; ++ ++ UINT32 vol_flag; ++ struct buffer_head *pbr_bh; ++ ++ UINT32 map_clu; ++ UINT32 map_sectors; ++ struct buffer_head **vol_amap; ++ ++ UINT16 **vol_utbl; ++ ++ UINT32 clu_srch_ptr; ++ UINT32 used_clusters; ++ UENTRY_T hint_uentry; ++ ++ UINT32 dev_ejected; ++ ++ FS_FUNC_T *fs_func; ++ ++ BUF_CACHE_T FAT_cache_array[FAT_CACHE_SIZE]; ++ BUF_CACHE_T FAT_cache_lru_list; ++ BUF_CACHE_T FAT_cache_hash_list[FAT_CACHE_HASH_SIZE]; ++ ++ BUF_CACHE_T buf_cache_array[BUF_CACHE_SIZE]; ++ BUF_CACHE_T buf_cache_lru_list; ++ BUF_CACHE_T buf_cache_hash_list[BUF_CACHE_HASH_SIZE]; ++ } FS_INFO_T; ++ ++#define ES_2_ENTRIES 2 ++#define ES_3_ENTRIES 3 ++#define ES_ALL_ENTRIES 0 ++ ++ typedef struct { ++ UINT32 sector; ++ INT32 offset; ++ INT32 alloc_flag; ++ UINT32 num_entries; ++ ++ void *__buf; ++ } ENTRY_SET_CACHE_T; ++ ++ INT32 ffsInit(void); ++ INT32 ffsShutdown(void); ++ ++ INT32 ffsMountVol(struct super_block *sb, INT32 drv); ++ INT32 ffsUmountVol(struct super_block *sb); ++ INT32 ffsCheckVol(struct super_block *sb); ++ INT32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info); ++ INT32 ffsSyncVol(struct super_block *sb, INT32 do_sync); ++ ++ INT32 ffsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid); ++ INT32 ffsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid); ++ INT32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount); ++ INT32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount); ++ INT32 ffsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size); ++ INT32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); ++ INT32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid); ++ INT32 ffsSetAttr(struct inode *inode, UINT32 attr); ++ INT32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info); ++ INT32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info); ++ INT32 ffsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu); ++ ++ INT32 ffsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid); ++ INT32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_ent); ++ INT32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid); ++ ++ INT32 fs_init(void); ++ INT32 fs_shutdown(void); ++ void fs_set_vol_flags(struct super_block *sb, UINT32 new_flag); ++ void fs_sync(struct super_block *sb, INT32 do_sync); ++ void fs_error(struct super_block *sb); ++ ++ INT32 clear_cluster(struct super_block *sb, UINT32 clu); ++ INT32 fat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain); ++ INT32 exfat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain); ++ void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse); ++ void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse); ++ UINT32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain); ++ INT32 count_num_clusters(struct super_block *sb, CHAIN_T *dir); ++ INT32 fat_count_used_clusters(struct super_block *sb); ++ INT32 exfat_count_used_clusters(struct super_block *sb); ++ void exfat_chain_cont_cluster(struct super_block *sb, UINT32 chain, INT32 len); ++ ++ INT32 load_alloc_bitmap(struct super_block *sb); ++ void free_alloc_bitmap(struct super_block *sb); ++ INT32 set_alloc_bitmap(struct super_block *sb, UINT32 clu); ++ INT32 clr_alloc_bitmap(struct super_block *sb, UINT32 clu); ++ UINT32 test_alloc_bitmap(struct super_block *sb, UINT32 clu); ++ void sync_alloc_bitmap(struct super_block *sb); ++ ++ INT32 load_upcase_table(struct super_block *sb); ++ void free_upcase_table(struct super_block *sb); ++ ++ UINT32 fat_get_entry_type(DENTRY_T *p_entry); ++ UINT32 exfat_get_entry_type(DENTRY_T *p_entry); ++ void fat_set_entry_type(DENTRY_T *p_entry, UINT32 type); ++ void exfat_set_entry_type(DENTRY_T *p_entry, UINT32 type); ++ UINT32 fat_get_entry_attr(DENTRY_T *p_entry); ++ UINT32 exfat_get_entry_attr(DENTRY_T *p_entry); ++ void fat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr); ++ void exfat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr); ++ UINT8 fat_get_entry_flag(DENTRY_T *p_entry); ++ UINT8 exfat_get_entry_flag(DENTRY_T *p_entry); ++ void fat_set_entry_flag(DENTRY_T *p_entry, UINT8 flag); ++ void exfat_set_entry_flag(DENTRY_T *p_entry, UINT8 flag); ++ UINT32 fat_get_entry_clu0(DENTRY_T *p_entry); ++ UINT32 exfat_get_entry_clu0(DENTRY_T *p_entry); ++ void fat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu); ++ void exfat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu); ++ UINT64 fat_get_entry_size(DENTRY_T *p_entry); ++ UINT64 exfat_get_entry_size(DENTRY_T *p_entry); ++ void fat_set_entry_size(DENTRY_T *p_entry, UINT64 size); ++ void exfat_set_entry_size(DENTRY_T *p_entry, UINT64 size); ++ void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); ++ void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); ++ void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); ++ void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); ++ INT32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, UINT32 start_clu, UINT64 size); ++ INT32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, UINT32 start_clu, UINT64 size); ++ INT32 fat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); ++ INT32 exfat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); ++ void init_dos_entry(DOS_DENTRY_T *ep, UINT32 type, UINT32 start_clu); ++ void init_ext_entry(EXT_DENTRY_T *ep, INT32 order, UINT8 chksum, UINT16 *uniname); ++ void init_file_entry(FILE_DENTRY_T *ep, UINT32 type); ++ void init_strm_entry(STRM_DENTRY_T *ep, UINT8 flags, UINT32 start_clu, UINT64 size); ++ void init_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname); ++ void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries); ++ void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries); ++ ++ INT32 find_location(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector, INT32 *offset); ++ DENTRY_T *get_entry_with_sector(struct super_block *sb, UINT32 sector, INT32 offset); ++ DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector); ++ ENTRY_SET_CACHE_T *get_entry_set_in_dir (struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, DENTRY_T **file_ep); ++ void release_entry_set (ENTRY_SET_CACHE_T *es); ++ INT32 write_whole_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es); ++ INT32 write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, UINT32 count); ++ INT32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 num_entries); ++ INT32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, INT32 num_entries); ++ INT32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type); ++ INT32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type); ++ INT32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry); ++ INT32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry); ++ INT32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, UINT32 type); ++ void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, INT32 entry); ++ void update_dir_checksum_with_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es); ++ BOOL is_dir_empty(struct super_block *sb, CHAIN_T *p_dir); ++ ++ INT32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 *entries, DOS_NAME_T *p_dosname); ++ void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, UINT8 mode); ++ void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname); ++ void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname); ++ INT32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, UINT16 *uniname, INT32 order); ++ INT32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname, INT32 order); ++ INT32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname); ++ void fat_attach_count_to_dos_name(UINT8 *dosname, INT32 count); ++ INT32 fat_calc_num_entries(UNI_NAME_T *p_uniname); ++ INT32 exfat_calc_num_entries(UNI_NAME_T *p_uniname); ++ UINT8 calc_checksum_1byte(void *data, INT32 len, UINT8 chksum); ++ UINT16 calc_checksum_2byte(void *data, INT32 len, UINT16 chksum, INT32 type); ++ UINT32 calc_checksum_4byte(void *data, INT32 len, UINT32 chksum, INT32 type); ++ ++ INT32 resolve_path(struct inode *inode, UINT8 *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname); ++ INT32 resolve_name(UINT8 *name, UINT8 **arg); ++ ++ INT32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); ++ INT32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); ++ INT32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); ++ INT32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid); ++ INT32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, UINT8 mode, FILE_ID_T *fid); ++ void remove_file(struct inode *inode, CHAIN_T *p_dir, INT32 entry); ++ INT32 rename_file(struct inode *inode, CHAIN_T *p_dir, INT32 old_entry, UNI_NAME_T *p_uniname, FILE_ID_T *fid); ++ INT32 move_file(struct inode *inode, CHAIN_T *p_olddir, INT32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid); ++ ++ INT32 sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 read); ++ INT32 sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 sync); ++ INT32 multi_sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 num_secs, INT32 read); ++ INT32 multi_sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 num_secs, INT32 sync); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/drivers/staging/exfat/exfat_api.c b/drivers/staging/exfat/exfat_api.c +new file mode 100644 +index 00000000..84a0b434 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_api.c +@@ -0,0 +1,457 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include <linux/version.h> ++#include <linux/module.h> ++#include <linux/init.h> ++ ++#include "exfat_version.h" ++#include "exfat_config.h" ++#include "exfat_global.h" ++#include "exfat_data.h" ++#include "exfat_oal.h" ++ ++#include "exfat_part.h" ++#include "exfat_nls.h" ++#include "exfat_api.h" ++#include "exfat_super.h" ++#include "exfat.h" ++ ++extern FS_STRUCT_T fs_struct[]; ++ ++extern struct semaphore z_sem; ++ ++INT32 FsInit(void) ++{ ++ INT32 i; ++ ++ for (i = 0; i < MAX_DRIVE; i++) { ++ fs_struct[i].mounted = FALSE; ++ fs_struct[i].sb = NULL; ++ sm_init(&(fs_struct[i].v_sem)); ++ } ++ ++ return(ffsInit()); ++} ++ ++INT32 FsShutdown(void) ++{ ++ INT32 i; ++ ++ for (i = 0; i < MAX_DRIVE; i++) { ++ if (!fs_struct[i].mounted) continue; ++ ++ ffsUmountVol(fs_struct[i].sb); ++ } ++ ++ return(ffsShutdown()); ++} ++ ++INT32 FsMountVol(struct super_block *sb) ++{ ++ INT32 err, drv; ++ ++ sm_P(&z_sem); ++ ++ for (drv = 0; drv < MAX_DRIVE; drv++) { ++ if (!fs_struct[drv].mounted) break; ++ } ++ ++ if (drv >= MAX_DRIVE) return(FFS_ERROR); ++ ++ sm_P(&(fs_struct[drv].v_sem)); ++ ++ err = buf_init(sb); ++ if (!err) { ++ err = ffsMountVol(sb, drv); ++ } ++ ++ sm_V(&(fs_struct[drv].v_sem)); ++ ++ if (!err) { ++ fs_struct[drv].mounted = TRUE; ++ fs_struct[drv].sb = sb; ++ } else { ++ buf_shutdown(sb); ++ } ++ ++ sm_V(&z_sem); ++ ++ return(err); ++} ++ ++INT32 FsUmountVol(struct super_block *sb) ++{ ++ INT32 err; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ sm_P(&z_sem); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsUmountVol(sb); ++ buf_shutdown(sb); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ fs_struct[p_fs->drv].mounted = FALSE; ++ fs_struct[p_fs->drv].sb = NULL; ++ ++ sm_V(&z_sem); ++ ++ return(err); ++} ++ ++INT32 FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info) ++{ ++ INT32 err; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (info == NULL) return(FFS_ERROR); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsGetVolInfo(sb, info); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsSyncVol(struct super_block *sb, INT32 do_sync) ++{ ++ INT32 err; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsSyncVol(sb, do_sync); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid) ++{ ++ INT32 err; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if ((fid == NULL) || (path == NULL) || (STRLEN(path) == 0)) ++ return(FFS_ERROR); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsLookupFile(inode, path, fid); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid) ++{ ++ INT32 err; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if ((fid == NULL) || (path == NULL) || (STRLEN(path) == 0)) ++ return(FFS_ERROR); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsCreateFile(inode, path, mode, fid); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount) ++{ ++ INT32 err; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (fid == NULL) return(FFS_INVALIDFID); ++ ++ if (buffer == NULL) return(FFS_ERROR); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsReadFile(inode, fid, buffer, count, rcount); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount) ++{ ++ INT32 err; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (fid == NULL) return(FFS_INVALIDFID); ++ ++ if (buffer == NULL) return(FFS_ERROR); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsWriteFile(inode, fid, buffer, count, wcount); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size) ++{ ++ INT32 err; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ PRINTK("FsTruncateFile entered (inode %p size %llu)\n", inode, new_size); ++ ++ err = ffsTruncateFile(inode, old_size, new_size); ++ ++ PRINTK("FsTruncateFile exitted (%d)\n", err); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) ++{ ++ INT32 err; ++ struct super_block *sb = old_parent_inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (fid == NULL) return(FFS_INVALIDFID); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsMoveFile(old_parent_inode, fid, new_parent_inode, new_dentry); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsRemoveFile(struct inode *inode, FILE_ID_T *fid) ++{ ++ INT32 err; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (fid == NULL) return(FFS_INVALIDFID); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsRemoveFile(inode, fid); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsSetAttr(struct inode *inode, UINT32 attr) ++{ ++ INT32 err; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsSetAttr(inode, attr); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsReadStat(struct inode *inode, DIR_ENTRY_T *info) ++{ ++ INT32 err; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsGetStat(inode, info); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsWriteStat(struct inode *inode, DIR_ENTRY_T *info) ++{ ++ INT32 err; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ PRINTK("FsWriteStat entered (inode %p info %p\n", inode, info); ++ ++ err = ffsSetStat(inode, info); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ PRINTK("FsWriteStat exited (%d)\n", err); ++ ++ return(err); ++} ++ ++INT32 FsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu) ++{ ++ INT32 err; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (clu == NULL) return(FFS_ERROR); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsMapCluster(inode, clu_offset, clu); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid) ++{ ++ INT32 err; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if ((fid == NULL) || (path == NULL) || (STRLEN(path) == 0)) ++ return(FFS_ERROR); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsCreateDir(inode, path, fid); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) ++{ ++ INT32 err; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (dir_entry == NULL) return(FFS_ERROR); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsReadDir(inode, dir_entry); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++INT32 FsRemoveDir(struct inode *inode, FILE_ID_T *fid) ++{ ++ INT32 err; ++ struct super_block *sb = inode->i_sb; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (fid == NULL) return(FFS_INVALIDFID); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ err = ffsRemoveDir(inode, fid); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return(err); ++} ++ ++EXPORT_SYMBOL(FsMountVol); ++EXPORT_SYMBOL(FsUmountVol); ++EXPORT_SYMBOL(FsGetVolInfo); ++EXPORT_SYMBOL(FsSyncVol); ++EXPORT_SYMBOL(FsLookupFile); ++EXPORT_SYMBOL(FsCreateFile); ++EXPORT_SYMBOL(FsReadFile); ++EXPORT_SYMBOL(FsWriteFile); ++EXPORT_SYMBOL(FsTruncateFile); ++EXPORT_SYMBOL(FsMoveFile); ++EXPORT_SYMBOL(FsRemoveFile); ++EXPORT_SYMBOL(FsSetAttr); ++EXPORT_SYMBOL(FsReadStat); ++EXPORT_SYMBOL(FsWriteStat); ++EXPORT_SYMBOL(FsMapCluster); ++EXPORT_SYMBOL(FsCreateDir); ++EXPORT_SYMBOL(FsReadDir); ++EXPORT_SYMBOL(FsRemoveDir); ++ ++#if EXFAT_CONFIG_KERNEL_DEBUG ++INT32 FsReleaseCache(struct super_block *sb) ++{ ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ sm_P(&(fs_struct[p_fs->drv].v_sem)); ++ ++ FAT_release_all(sb); ++ buf_release_all(sb); ++ ++ sm_V(&(fs_struct[p_fs->drv].v_sem)); ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL(FsReleaseCache); ++#endif ++ ++static int __init init_exfat_core(void) ++{ ++ int err; ++ ++ printk(KERN_INFO "exFAT: Core Version %s\n", EXFAT_VERSION); ++ ++ err = FsInit(); ++ if (err) { ++ if (err == FFS_MEMORYERR) ++ return -ENOMEM; ++ else ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static void __exit exit_exfat_core(void) ++{ ++ FsShutdown(); ++} ++ ++module_init(init_exfat_core); ++module_exit(exit_exfat_core); ++ ++MODULE_LICENSE("GPL"); +diff --git a/drivers/staging/exfat/exfat_api.h b/drivers/staging/exfat/exfat_api.h +new file mode 100644 +index 00000000..8de658b1 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_api.h +@@ -0,0 +1,165 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _EXFAT_API_H ++#define _EXFAT_API_H ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define EXFAT_SUPER_MAGIC (0x2011BAB0L) ++#define EXFAT_ROOT_INO 1 ++ ++#define FAT12 0x01 ++#define FAT16 0x0E ++#define FAT32 0x0C ++#define EXFAT 0x07 ++ ++#define MAX_CHARSET_SIZE 3 ++#define MAX_PATH_DEPTH 15 ++#define MAX_NAME_LENGTH 256 ++#define MAX_PATH_LENGTH 260 ++#define DOS_NAME_LENGTH 11 ++#define DOS_PATH_LENGTH 80 ++ ++#define ATTR_NORMAL 0x0000 ++#define ATTR_READONLY 0x0001 ++#define ATTR_HIDDEN 0x0002 ++#define ATTR_SYSTEM 0x0004 ++#define ATTR_VOLUME 0x0008 ++#define ATTR_SUBDIR 0x0010 ++#define ATTR_ARCHIVE 0x0020 ++#define ATTR_SYMLINK 0x0040 ++#define ATTR_EXTEND 0x000F ++#define ATTR_RWMASK 0x007E ++ ++#define FM_REGULAR 0x00 ++#define FM_SYMLINK 0x40 ++ ++#define FFS_SUCCESS 0 ++#define FFS_MEDIAERR 1 ++#define FFS_FORMATERR 2 ++#define FFS_MOUNTED 3 ++#define FFS_NOTMOUNTED 4 ++#define FFS_ALIGNMENTERR 5 ++#define FFS_SEMAPHOREERR 6 ++#define FFS_INVALIDPATH 7 ++#define FFS_INVALIDFID 8 ++#define FFS_NOTFOUND 9 ++#define FFS_FILEEXIST 10 ++#define FFS_PERMISSIONERR 11 ++#define FFS_NOTOPENED 12 ++#define FFS_MAXOPENED 13 ++#define FFS_FULL 14 ++#define FFS_EOF 15 ++#define FFS_DIRBUSY 16 ++#define FFS_MEMORYERR 17 ++#define FFS_NAMETOOLONG 18 ++#define FFS_ERROR 19 ++ ++ typedef struct { ++ UINT16 Year; ++ UINT16 Month; ++ UINT16 Day; ++ UINT16 Hour; ++ UINT16 Minute; ++ UINT16 Second; ++ UINT16 MilliSecond; ++ } DATE_TIME_T; ++ ++ typedef struct { ++ UINT32 Offset; ++ UINT32 Size; ++ } PART_INFO_T; ++ ++ typedef struct { ++ UINT32 SecSize; ++ UINT32 DevSize; ++ } DEV_INFO_T; ++ ++ typedef struct { ++ UINT32 FatType; ++ UINT32 ClusterSize; ++ UINT32 NumClusters; ++ UINT32 FreeClusters; ++ UINT32 UsedClusters; ++ } VOL_INFO_T; ++ ++ typedef struct { ++ UINT32 dir; ++ INT32 size; ++ UINT8 flags; ++ } CHAIN_T; ++ ++ typedef struct { ++ CHAIN_T dir; ++ INT32 entry; ++ UINT32 type; ++ UINT32 attr; ++ UINT32 start_clu; ++ UINT64 size; ++ UINT8 flags; ++ INT64 rwoffset; ++ INT32 hint_last_off; ++ UINT32 hint_last_clu; ++ } FILE_ID_T; ++ ++ typedef struct { ++ INT8 Name[MAX_NAME_LENGTH *MAX_CHARSET_SIZE]; ++ INT8 ShortName[DOS_NAME_LENGTH + 2]; ++ UINT32 Attr; ++ UINT64 Size; ++ UINT32 NumSubdirs; ++ DATE_TIME_T CreateTimestamp; ++ DATE_TIME_T ModifyTimestamp; ++ DATE_TIME_T AccessTimestamp; ++ } DIR_ENTRY_T; ++ ++ INT32 FsInit(void); ++ INT32 FsShutdown(void); ++ ++ INT32 FsMountVol(struct super_block *sb); ++ INT32 FsUmountVol(struct super_block *sb); ++ INT32 FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info); ++ INT32 FsSyncVol(struct super_block *sb, INT32 do_sync); ++ ++ INT32 FsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid); ++ INT32 FsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid); ++ INT32 FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount); ++ INT32 FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount); ++ INT32 FsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size); ++ INT32 FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); ++ INT32 FsRemoveFile(struct inode *inode, FILE_ID_T *fid); ++ INT32 FsSetAttr(struct inode *inode, UINT32 attr); ++ INT32 FsReadStat(struct inode *inode, DIR_ENTRY_T *info); ++ INT32 FsWriteStat(struct inode *inode, DIR_ENTRY_T *info); ++ INT32 FsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu); ++ ++ INT32 FsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid); ++ INT32 FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry); ++ INT32 FsRemoveDir(struct inode *inode, FILE_ID_T *fid); ++ ++ INT32 FsReleaseCache(struct super_block *sb); ++#ifdef __cplusplus ++} ++#endif ++#endif +diff --git a/drivers/staging/exfat/exfat_blkdev.c b/drivers/staging/exfat/exfat_blkdev.c +new file mode 100644 +index 00000000..c5bf7a7a +--- /dev/null ++++ b/drivers/staging/exfat/exfat_blkdev.c +@@ -0,0 +1,156 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include <linux/blkdev.h> ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++#include "exfat_blkdev.h" ++#include "exfat_data.h" ++#include "exfat_api.h" ++#include "exfat_super.h" ++ ++INT32 bdev_init(void) ++{ ++ return(FFS_SUCCESS); ++} ++ ++INT32 bdev_shutdown(void) ++{ ++ return(FFS_SUCCESS); ++} ++ ++INT32 bdev_open(struct super_block *sb) ++{ ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ if (p_bd->opened) return(FFS_SUCCESS); ++ ++ p_bd->sector_size = bdev_logical_block_size(sb->s_bdev); ++ p_bd->sector_size_bits = my_log2(p_bd->sector_size); ++ p_bd->sector_size_mask = p_bd->sector_size - 1; ++ p_bd->num_sectors = i_size_read(sb->s_bdev->bd_inode) >> p_bd->sector_size_bits; ++ ++ p_bd->opened = TRUE; ++ ++ return(FFS_SUCCESS); ++} ++ ++INT32 bdev_close(struct super_block *sb) ++{ ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ if (!p_bd->opened) return(FFS_SUCCESS); ++ ++ p_bd->opened = FALSE; ++ return(FFS_SUCCESS); ++} ++ ++INT32 bdev_read(struct super_block *sb, UINT32 secno, struct buffer_head **bh, UINT32 num_secs, INT32 read) ++{ ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++#if EXFAT_CONFIG_KERNEL_DEBUG ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ long flags = sbi->debug_flags; ++ ++ if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR); ++#endif ++ ++ if (!p_bd->opened) return(FFS_MEDIAERR); ++ ++ if (*bh) __brelse(*bh); ++ ++ if (read) ++ *bh = __bread(sb->s_bdev, secno, num_secs << p_bd->sector_size_bits); ++ else ++ *bh = __getblk(sb->s_bdev, secno, num_secs << p_bd->sector_size_bits); ++ ++ if (*bh) return(FFS_SUCCESS); ++ ++ WARN(!p_fs->dev_ejected, ++ "[EXFAT] No bh, device seems wrong or to be ejected.\n"); ++ ++ return(FFS_MEDIAERR); ++} ++ ++INT32 bdev_write(struct super_block *sb, UINT32 secno, struct buffer_head *bh, UINT32 num_secs, INT32 sync) ++{ ++ INT32 count; ++ struct buffer_head *bh2; ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++#if EXFAT_CONFIG_KERNEL_DEBUG ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ long flags = sbi->debug_flags; ++ ++ if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR); ++#endif ++ ++ if (!p_bd->opened) return(FFS_MEDIAERR); ++ ++ if (secno == bh->b_blocknr) { ++ lock_buffer(bh); ++ set_buffer_uptodate(bh); ++ mark_buffer_dirty(bh); ++ unlock_buffer(bh); ++ if (sync && (sync_dirty_buffer(bh) != 0)) ++ return (FFS_MEDIAERR); ++ } else { ++ count = num_secs << p_bd->sector_size_bits; ++ ++ bh2 = __getblk(sb->s_bdev, secno, count); ++ ++ if (bh2 == NULL) ++ goto no_bh; ++ ++ lock_buffer(bh2); ++ MEMCPY(bh2->b_data, bh->b_data, count); ++ set_buffer_uptodate(bh2); ++ mark_buffer_dirty(bh2); ++ unlock_buffer(bh2); ++ if (sync && (sync_dirty_buffer(bh2) != 0)) { ++ __brelse(bh2); ++ goto no_bh; ++ } ++ __brelse(bh2); ++ } ++ ++ return(FFS_SUCCESS); ++ ++no_bh: ++ WARN(!p_fs->dev_ejected, ++ "[EXFAT] No bh, device seems wrong or to be ejected.\n"); ++ ++ return (FFS_MEDIAERR); ++} ++ ++INT32 bdev_sync(struct super_block *sb) ++{ ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++#if EXFAT_CONFIG_KERNEL_DEBUG ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ long flags = sbi->debug_flags; ++ ++ if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR); ++#endif ++ ++ if (!p_bd->opened) return(FFS_MEDIAERR); ++ ++ return sync_blockdev(sb->s_bdev); ++} +diff --git a/drivers/staging/exfat/exfat_blkdev.h b/drivers/staging/exfat/exfat_blkdev.h +new file mode 100644 +index 00000000..7710dd63 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_blkdev.h +@@ -0,0 +1,46 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _EXFAT_BLKDEV_H ++#define _EXFAT_BLKDEV_H ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ typedef struct __BD_INFO_T { ++ INT32 sector_size; ++ INT32 sector_size_bits; ++ INT32 sector_size_mask; ++ INT32 num_sectors; ++ BOOL opened; ++ } BD_INFO_T; ++ ++ INT32 bdev_init(void); ++ INT32 bdev_shutdown(void); ++ INT32 bdev_open(struct super_block *sb); ++ INT32 bdev_close(struct super_block *sb); ++ INT32 bdev_read(struct super_block *sb, UINT32 secno, struct buffer_head **bh, UINT32 num_secs, INT32 read); ++ INT32 bdev_write(struct super_block *sb, UINT32 secno, struct buffer_head *bh, UINT32 num_secs, INT32 sync); ++ INT32 bdev_sync(struct super_block *sb); ++#ifdef __cplusplus ++} ++#endif ++#endif +diff --git a/drivers/staging/exfat/exfat_cache.c b/drivers/staging/exfat/exfat_cache.c +new file mode 100644 +index 00000000..008629fb +--- /dev/null ++++ b/drivers/staging/exfat/exfat_cache.c +@@ -0,0 +1,740 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++#include "exfat_data.h" ++ ++#include "exfat_cache.h" ++#include "exfat_super.h" ++#include "exfat.h" ++ ++extern FS_STRUCT_T fs_struct[]; ++ ++#define sm_P(s) ++#define sm_V(s) ++ ++static INT32 __FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content); ++static INT32 __FAT_write(struct super_block *sb, UINT32 loc, UINT32 content); ++ ++static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, UINT32 sec); ++static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, UINT32 sec); ++static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); ++static void FAT_cache_remove_hash(BUF_CACHE_T *bp); ++ ++static UINT8 *__buf_getblk(struct super_block *sb, UINT32 sec); ++ ++static BUF_CACHE_T *buf_cache_find(struct super_block *sb, UINT32 sec); ++static BUF_CACHE_T *buf_cache_get(struct super_block *sb, UINT32 sec); ++static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); ++static void buf_cache_remove_hash(BUF_CACHE_T *bp); ++ ++static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); ++static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); ++static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); ++static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); ++ ++INT32 buf_init(struct super_block *sb) ++{ ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ INT32 i; ++ ++ p_fs->FAT_cache_lru_list.next = p_fs->FAT_cache_lru_list.prev = &p_fs->FAT_cache_lru_list; ++ ++ for (i = 0; i < FAT_CACHE_SIZE; i++) { ++ p_fs->FAT_cache_array[i].drv = -1; ++ p_fs->FAT_cache_array[i].sec = ~0; ++ p_fs->FAT_cache_array[i].flag = 0; ++ p_fs->FAT_cache_array[i].buf_bh = NULL; ++ p_fs->FAT_cache_array[i].prev = p_fs->FAT_cache_array[i].next = NULL; ++ push_to_mru(&(p_fs->FAT_cache_array[i]), &p_fs->FAT_cache_lru_list); ++ } ++ ++ p_fs->buf_cache_lru_list.next = p_fs->buf_cache_lru_list.prev = &p_fs->buf_cache_lru_list; ++ ++ for (i = 0; i < BUF_CACHE_SIZE; i++) { ++ p_fs->buf_cache_array[i].drv = -1; ++ p_fs->buf_cache_array[i].sec = ~0; ++ p_fs->buf_cache_array[i].flag = 0; ++ p_fs->buf_cache_array[i].buf_bh = NULL; ++ p_fs->buf_cache_array[i].prev = p_fs->buf_cache_array[i].next = NULL; ++ push_to_mru(&(p_fs->buf_cache_array[i]), &p_fs->buf_cache_lru_list); ++ } ++ ++ for (i = 0; i < FAT_CACHE_HASH_SIZE; i++) { ++ p_fs->FAT_cache_hash_list[i].drv = -1; ++ p_fs->FAT_cache_hash_list[i].sec = ~0; ++ p_fs->FAT_cache_hash_list[i].hash_next = p_fs->FAT_cache_hash_list[i].hash_prev = &(p_fs->FAT_cache_hash_list[i]); ++ } ++ ++ for (i = 0; i < FAT_CACHE_SIZE; i++) { ++ FAT_cache_insert_hash(sb, &(p_fs->FAT_cache_array[i])); ++ } ++ ++ for (i = 0; i < BUF_CACHE_HASH_SIZE; i++) { ++ p_fs->buf_cache_hash_list[i].drv = -1; ++ p_fs->buf_cache_hash_list[i].sec = ~0; ++ p_fs->buf_cache_hash_list[i].hash_next = p_fs->buf_cache_hash_list[i].hash_prev = &(p_fs->buf_cache_hash_list[i]); ++ } ++ ++ for (i = 0; i < BUF_CACHE_SIZE; i++) { ++ buf_cache_insert_hash(sb, &(p_fs->buf_cache_array[i])); ++ } ++ ++ return(FFS_SUCCESS); ++} ++ ++INT32 buf_shutdown(struct super_block *sb) ++{ ++ return(FFS_SUCCESS); ++} ++ ++INT32 FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content) ++{ ++ INT32 ret; ++ ++ sm_P(&f_sem); ++ ++ ret = __FAT_read(sb, loc, content); ++ ++ sm_V(&f_sem); ++ ++ return(ret); ++} ++ ++INT32 FAT_write(struct super_block *sb, UINT32 loc, UINT32 content) ++{ ++ INT32 ret; ++ ++ sm_P(&f_sem); ++ ++ ret = __FAT_write(sb, loc, content); ++ ++ sm_V(&f_sem); ++ ++ return(ret); ++} ++ ++static INT32 __FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content) ++{ ++ INT32 off; ++ UINT32 sec, _content; ++ UINT8 *fat_sector, *fat_entry; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ if (p_fs->vol_type == FAT12) { ++ sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); ++ off = (loc + (loc >> 1)) & p_bd->sector_size_mask; ++ ++ if (off == (p_bd->sector_size-1)) { ++ fat_sector = FAT_getblk(sb, sec); ++ if (!fat_sector) ++ return -1; ++ ++ _content = (UINT32) fat_sector[off]; ++ ++ fat_sector = FAT_getblk(sb, ++sec); ++ if (!fat_sector) ++ return -1; ++ ++ _content |= (UINT32) fat_sector[0] << 8; ++ } else { ++ fat_sector = FAT_getblk(sb, sec); ++ if (!fat_sector) ++ return -1; ++ ++ fat_entry = &(fat_sector[off]); ++ _content = GET16(fat_entry); ++ } ++ ++ if (loc & 1) _content >>= 4; ++ ++ _content &= 0x00000FFF; ++ ++ if (_content >= CLUSTER_16(0x0FF8)) { ++ *content = CLUSTER_32(~0); ++ return 0; ++ } else { ++ *content = CLUSTER_32(_content); ++ return 0; ++ } ++ } else if (p_fs->vol_type == FAT16) { ++ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); ++ off = (loc << 1) & p_bd->sector_size_mask; ++ ++ fat_sector = FAT_getblk(sb, sec); ++ if ++ (!fat_sector) ++ return -1; ++ ++ fat_entry = &(fat_sector[off]); ++ ++ _content = GET16_A(fat_entry); ++ ++ _content &= 0x0000FFFF; ++ ++ if (_content >= CLUSTER_16(0xFFF8)) { ++ *content = CLUSTER_32(~0); ++ return 0; ++ } else { ++ *content = CLUSTER_32(_content); ++ return 0; ++ } ++ } else if (p_fs->vol_type == FAT32) { ++ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); ++ off = (loc << 2) & p_bd->sector_size_mask; ++ ++ fat_sector = FAT_getblk(sb, sec); ++ if (!fat_sector) ++ return -1; ++ ++ fat_entry = &(fat_sector[off]); ++ ++ _content = GET32_A(fat_entry); ++ ++ _content &= 0x0FFFFFFF; ++ ++ if (_content >= CLUSTER_32(0x0FFFFFF8)) { ++ *content = CLUSTER_32(~0); ++ return 0; ++ } else { ++ *content = CLUSTER_32(_content); ++ return 0; ++ } ++ } else { ++ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); ++ off = (loc << 2) & p_bd->sector_size_mask; ++ ++ fat_sector = FAT_getblk(sb, sec); ++ if (!fat_sector) ++ return -1; ++ ++ fat_entry = &(fat_sector[off]); ++ _content = GET32_A(fat_entry); ++ ++ if (_content >= CLUSTER_32(0xFFFFFFF8)) { ++ *content = CLUSTER_32(~0); ++ return 0; ++ } else { ++ *content = CLUSTER_32(_content); ++ return 0; ++ } ++ } ++ ++ *content = CLUSTER_32(~0); ++ return 0; ++} ++ ++static INT32 __FAT_write(struct super_block *sb, UINT32 loc, UINT32 content) ++{ ++ INT32 off; ++ UINT32 sec; ++ UINT8 *fat_sector, *fat_entry; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ ++ if (p_fs->vol_type == FAT12) { ++ ++ content &= 0x00000FFF; ++ ++ sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); ++ off = (loc + (loc >> 1)) & p_bd->sector_size_mask; ++ ++ fat_sector = FAT_getblk(sb, sec); ++ if (!fat_sector) ++ return -1; ++ ++ if (loc & 1) { ++ ++ content <<= 4; ++ ++ if (off == (p_bd->sector_size-1)) { ++ fat_sector[off] = (UINT8)(content | (fat_sector[off] & 0x0F)); ++ FAT_modify(sb, sec); ++ ++ fat_sector = FAT_getblk(sb, ++sec); ++ if (!fat_sector) ++ return -1; ++ ++ fat_sector[0] = (UINT8)(content >> 8); ++ } else { ++ fat_entry = &(fat_sector[off]); ++ content |= GET16(fat_entry) & 0x000F; ++ ++ SET16(fat_entry, content); ++ } ++ } else { ++ fat_sector[off] = (UINT8)(content); ++ ++ if (off == (p_bd->sector_size-1)) { ++ fat_sector[off] = (UINT8)(content); ++ FAT_modify(sb, sec); ++ ++ fat_sector = FAT_getblk(sb, ++sec); ++ fat_sector[0] = (UINT8)((fat_sector[0] & 0xF0) | (content >> 8)); ++ } else { ++ fat_entry = &(fat_sector[off]); ++ content |= GET16(fat_entry) & 0xF000; ++ ++ SET16(fat_entry, content); ++ } ++ } ++ } ++ ++ else if (p_fs->vol_type == FAT16) { ++ ++ content &= 0x0000FFFF; ++ ++ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); ++ off = (loc << 1) & p_bd->sector_size_mask; ++ ++ fat_sector = FAT_getblk(sb, sec); ++ if (!fat_sector) ++ return -1; ++ ++ fat_entry = &(fat_sector[off]); ++ ++ SET16_A(fat_entry, content); ++ } ++ ++ else if (p_fs->vol_type == FAT32) { ++ ++ content &= 0x0FFFFFFF; ++ ++ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); ++ off = (loc << 2) & p_bd->sector_size_mask; ++ ++ fat_sector = FAT_getblk(sb, sec); ++ if (!fat_sector) ++ return -1; ++ ++ fat_entry = &(fat_sector[off]); ++ ++ content |= GET32_A(fat_entry) & 0xF0000000; ++ ++ SET32_A(fat_entry, content); ++ } ++ ++ else { ++ ++ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); ++ off = (loc << 2) & p_bd->sector_size_mask; ++ ++ fat_sector = FAT_getblk(sb, sec); ++ if (!fat_sector) ++ return -1; ++ ++ fat_entry = &(fat_sector[off]); ++ ++ SET32_A(fat_entry, content); ++ } ++ ++ FAT_modify(sb, sec); ++ return 0; ++} ++ ++UINT8 *FAT_getblk(struct super_block *sb, UINT32 sec) ++{ ++ BUF_CACHE_T *bp; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ bp = FAT_cache_find(sb, sec); ++ if (bp != NULL) { ++ move_to_mru(bp, &p_fs->FAT_cache_lru_list); ++ return(bp->buf_bh->b_data); ++ } ++ ++ bp = FAT_cache_get(sb, sec); ++ ++ FAT_cache_remove_hash(bp); ++ ++ bp->drv = p_fs->drv; ++ bp->sec = sec; ++ bp->flag = 0; ++ ++ FAT_cache_insert_hash(sb, bp); ++ ++ if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { ++ FAT_cache_remove_hash(bp); ++ bp->drv = -1; ++ bp->sec = ~0; ++ bp->flag = 0; ++ bp->buf_bh = NULL; ++ ++ move_to_lru(bp, &p_fs->FAT_cache_lru_list); ++ return NULL; ++ } ++ ++ return(bp->buf_bh->b_data); ++} ++ ++void FAT_modify(struct super_block *sb, UINT32 sec) ++{ ++ BUF_CACHE_T *bp; ++ ++ bp = FAT_cache_find(sb, sec); ++ if (bp != NULL) { ++ sector_write(sb, sec, bp->buf_bh, 0); ++ } ++} ++ ++void FAT_release_all(struct super_block *sb) ++{ ++ BUF_CACHE_T *bp; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ sm_P(&f_sem); ++ ++ bp = p_fs->FAT_cache_lru_list.next; ++ while (bp != &p_fs->FAT_cache_lru_list) { ++ if (bp->drv == p_fs->drv) { ++ bp->drv = -1; ++ bp->sec = ~0; ++ bp->flag = 0; ++ ++ if(bp->buf_bh) { ++ __brelse(bp->buf_bh); ++ bp->buf_bh = NULL; ++ } ++ } ++ bp = bp->next; ++ } ++ ++ sm_V(&f_sem); ++} ++ ++void FAT_sync(struct super_block *sb) ++{ ++ BUF_CACHE_T *bp; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ sm_P(&f_sem); ++ ++ bp = p_fs->FAT_cache_lru_list.next; ++ while (bp != &p_fs->FAT_cache_lru_list) { ++ if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { ++ sync_dirty_buffer(bp->buf_bh); ++ bp->flag &= ~(DIRTYBIT); ++ } ++ bp = bp->next; ++ } ++ ++ sm_V(&f_sem); ++} ++ ++static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, UINT32 sec) ++{ ++ INT32 off; ++ BUF_CACHE_T *bp, *hp; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE - 1); ++ ++ hp = &(p_fs->FAT_cache_hash_list[off]); ++ for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { ++ if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { ++ ++ WARN(!bp->buf_bh, "[EXFAT] FAT_cache has no bh. " ++ "It will make system panic.\n"); ++ ++ touch_buffer(bp->buf_bh); ++ return(bp); ++ } ++ } ++ return(NULL); ++} ++ ++static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, UINT32 sec) ++{ ++ BUF_CACHE_T *bp; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ bp = p_fs->FAT_cache_lru_list.prev; ++ ++ ++ move_to_mru(bp, &p_fs->FAT_cache_lru_list); ++ return(bp); ++} ++ ++static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) ++{ ++ INT32 off; ++ BUF_CACHE_T *hp; ++ FS_INFO_T *p_fs; ++ ++ p_fs = &(EXFAT_SB(sb)->fs_info); ++ off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE-1); ++ ++ hp = &(p_fs->FAT_cache_hash_list[off]); ++ bp->hash_next = hp->hash_next; ++ bp->hash_prev = hp; ++ hp->hash_next->hash_prev = bp; ++ hp->hash_next = bp; ++} ++ ++static void FAT_cache_remove_hash(BUF_CACHE_T *bp) ++{ ++ (bp->hash_prev)->hash_next = bp->hash_next; ++ (bp->hash_next)->hash_prev = bp->hash_prev; ++} ++ ++UINT8 *buf_getblk(struct super_block *sb, UINT32 sec) ++{ ++ UINT8 *buf; ++ ++ sm_P(&b_sem); ++ ++ buf = __buf_getblk(sb, sec); ++ ++ sm_V(&b_sem); ++ ++ return(buf); ++} ++ ++static UINT8 *__buf_getblk(struct super_block *sb, UINT32 sec) ++{ ++ BUF_CACHE_T *bp; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ bp = buf_cache_find(sb, sec); ++ if (bp != NULL) { ++ move_to_mru(bp, &p_fs->buf_cache_lru_list); ++ return(bp->buf_bh->b_data); ++ } ++ ++ bp = buf_cache_get(sb, sec); ++ ++ buf_cache_remove_hash(bp); ++ ++ bp->drv = p_fs->drv; ++ bp->sec = sec; ++ bp->flag = 0; ++ ++ buf_cache_insert_hash(sb, bp); ++ ++ if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { ++ buf_cache_remove_hash(bp); ++ bp->drv = -1; ++ bp->sec = ~0; ++ bp->flag = 0; ++ bp->buf_bh = NULL; ++ ++ move_to_lru(bp, &p_fs->buf_cache_lru_list); ++ return NULL; ++ } ++ ++ return(bp->buf_bh->b_data); ++ ++} ++ ++void buf_modify(struct super_block *sb, UINT32 sec) ++{ ++ BUF_CACHE_T *bp; ++ ++ sm_P(&b_sem); ++ ++ bp = buf_cache_find(sb, sec); ++ if (likely(bp != NULL)) { ++ sector_write(sb, sec, bp->buf_bh, 0); ++ } ++ ++ WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); ++ ++ sm_V(&b_sem); ++} ++ ++void buf_lock(struct super_block *sb, UINT32 sec) ++{ ++ BUF_CACHE_T *bp; ++ ++ sm_P(&b_sem); ++ ++ bp = buf_cache_find(sb, sec); ++ if (likely(bp != NULL)) bp->flag |= LOCKBIT; ++ ++ WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); ++ ++ sm_V(&b_sem); ++} ++ ++void buf_unlock(struct super_block *sb, UINT32 sec) ++{ ++ BUF_CACHE_T *bp; ++ ++ sm_P(&b_sem); ++ ++ bp = buf_cache_find(sb, sec); ++ if (likely(bp != NULL)) bp->flag &= ~(LOCKBIT); ++ ++ WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); ++ ++ sm_V(&b_sem); ++} ++ ++void buf_release(struct super_block *sb, UINT32 sec) ++{ ++ BUF_CACHE_T *bp; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ sm_P(&b_sem); ++ ++ bp = buf_cache_find(sb, sec); ++ if (likely(bp != NULL)) { ++ bp->drv = -1; ++ bp->sec = ~0; ++ bp->flag = 0; ++ ++ if(bp->buf_bh) { ++ __brelse(bp->buf_bh); ++ bp->buf_bh = NULL; ++ } ++ ++ move_to_lru(bp, &p_fs->buf_cache_lru_list); ++ } ++ ++ sm_V(&b_sem); ++} ++ ++void buf_release_all(struct super_block *sb) ++{ ++ BUF_CACHE_T *bp; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ sm_P(&b_sem); ++ ++ bp = p_fs->buf_cache_lru_list.next; ++ while (bp != &p_fs->buf_cache_lru_list) { ++ if (bp->drv == p_fs->drv) { ++ bp->drv = -1; ++ bp->sec = ~0; ++ bp->flag = 0; ++ ++ if(bp->buf_bh) { ++ __brelse(bp->buf_bh); ++ bp->buf_bh = NULL; ++ } ++ } ++ bp = bp->next; ++ } ++ ++ sm_V(&b_sem); ++} ++ ++void buf_sync(struct super_block *sb) ++{ ++ BUF_CACHE_T *bp; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ sm_P(&b_sem); ++ ++ bp = p_fs->buf_cache_lru_list.next; ++ while (bp != &p_fs->buf_cache_lru_list) { ++ if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { ++ sync_dirty_buffer(bp->buf_bh); ++ bp->flag &= ~(DIRTYBIT); ++ } ++ bp = bp->next; ++ } ++ ++ sm_V(&b_sem); ++} ++ ++static BUF_CACHE_T *buf_cache_find(struct super_block *sb, UINT32 sec) ++{ ++ INT32 off; ++ BUF_CACHE_T *bp, *hp; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE - 1); ++ ++ hp = &(p_fs->buf_cache_hash_list[off]); ++ for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { ++ if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { ++ touch_buffer(bp->buf_bh); ++ return(bp); ++ } ++ } ++ return(NULL); ++} ++ ++static BUF_CACHE_T *buf_cache_get(struct super_block *sb, UINT32 sec) ++{ ++ BUF_CACHE_T *bp; ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ bp = p_fs->buf_cache_lru_list.prev; ++ while (bp->flag & LOCKBIT) bp = bp->prev; ++ ++ ++ move_to_mru(bp, &p_fs->buf_cache_lru_list); ++ return(bp); ++} ++ ++static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) ++{ ++ INT32 off; ++ BUF_CACHE_T *hp; ++ FS_INFO_T *p_fs; ++ ++ p_fs = &(EXFAT_SB(sb)->fs_info); ++ off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE-1); ++ ++ hp = &(p_fs->buf_cache_hash_list[off]); ++ bp->hash_next = hp->hash_next; ++ bp->hash_prev = hp; ++ hp->hash_next->hash_prev = bp; ++ hp->hash_next = bp; ++} ++ ++static void buf_cache_remove_hash(BUF_CACHE_T *bp) ++{ ++ (bp->hash_prev)->hash_next = bp->hash_next; ++ (bp->hash_next)->hash_prev = bp->hash_prev; ++} ++ ++static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list) ++{ ++ bp->next = list->next; ++ bp->prev = list; ++ list->next->prev = bp; ++ list->next = bp; ++} ++ ++static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list) ++{ ++ bp->prev = list->prev; ++ bp->next = list; ++ list->prev->next = bp; ++ list->prev = bp; ++} ++ ++static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list) ++{ ++ bp->prev->next = bp->next; ++ bp->next->prev = bp->prev; ++ push_to_mru(bp, list); ++} ++ ++static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list) ++{ ++ bp->prev->next = bp->next; ++ bp->next->prev = bp->prev; ++ push_to_lru(bp, list); ++} +diff --git a/drivers/staging/exfat/exfat_cache.h b/drivers/staging/exfat/exfat_cache.h +new file mode 100644 +index 00000000..76745095 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_cache.h +@@ -0,0 +1,63 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _EXFAT_CACHE_H ++#define _EXFAT_CACHE_H ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define LOCKBIT 0x01 ++#define DIRTYBIT 0x02 ++ ++ typedef struct __BUF_CACHE_T { ++ struct __BUF_CACHE_T *next; ++ struct __BUF_CACHE_T *prev; ++ struct __BUF_CACHE_T *hash_next; ++ struct __BUF_CACHE_T *hash_prev; ++ INT32 drv; ++ UINT32 sec; ++ UINT32 flag; ++ struct buffer_head *buf_bh; ++ } BUF_CACHE_T; ++ ++ INT32 buf_init(struct super_block *sb); ++ INT32 buf_shutdown(struct super_block *sb); ++ INT32 FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content); ++ INT32 FAT_write(struct super_block *sb, UINT32 loc, UINT32 content); ++ UINT8 *FAT_getblk(struct super_block *sb, UINT32 sec); ++ void FAT_modify(struct super_block *sb, UINT32 sec); ++ void FAT_release_all(struct super_block *sb); ++ void FAT_sync(struct super_block *sb); ++ UINT8 *buf_getblk(struct super_block *sb, UINT32 sec); ++ void buf_modify(struct super_block *sb, UINT32 sec); ++ void buf_lock(struct super_block *sb, UINT32 sec); ++ void buf_unlock(struct super_block *sb, UINT32 sec); ++ void buf_release(struct super_block *sb, UINT32 sec); ++ void buf_release_all(struct super_block *sb); ++ void buf_sync(struct super_block *sb); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/drivers/staging/exfat/exfat_config.h b/drivers/staging/exfat/exfat_config.h +new file mode 100644 +index 00000000..b3cd0112 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_config.h +@@ -0,0 +1,57 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _EXFAT_CONFIG_H ++#define _EXFAT_CONFIG_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define OS_NONOS 1 ++#define OS_LINUX 2 ++ ++#define FFS_CONFIG_OS OS_LINUX ++ ++#define FFS_CONFIG_LITTLE_ENDIAN 1 ++#define FFS_CONFIG_LEGACY_32BIT_API 0 ++#define FFS_CONFIG_LEGACY_32BIT_API 0 ++#define FFS_CONFIG_SUPPORT_CP1250 1 ++#define FFS_CONFIG_SUPPORT_CP1251 1 ++#define FFS_CONFIG_SUPPORT_CP1252 1 ++#define FFS_CONFIG_SUPPORT_CP1253 1 ++#define FFS_CONFIG_SUPPORT_CP1254 1 ++#define FFS_CONFIG_SUPPORT_CP1255 1 ++#define FFS_CONFIG_SUPPORT_CP1256 1 ++#define FFS_CONFIG_SUPPORT_CP1257 1 ++#define FFS_CONFIG_SUPPORT_CP1258 1 ++#define FFS_CONFIG_SUPPORT_CP874 1 ++#define FFS_CONFIG_SUPPORT_CP932 1 ++#define FFS_CONFIG_SUPPORT_CP936 1 ++#define FFS_CONFIG_SUPPORT_CP949 1 ++#define FFS_CONFIG_SUPPORT_CP950 1 ++#define FFS_CONFIG_SUPPORT_UTF8 1 ++#define EXFAT_CONFIG_DISCARD 1 ++#define EXFAT_CONFIG_KERNEL_DEBUG 1 ++#define EXFAT_CONFIG_DEBUG_MSG 0 ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/drivers/staging/exfat/exfat_data.c b/drivers/staging/exfat/exfat_data.c +new file mode 100644 +index 00000000..6c58eac8 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_data.c +@@ -0,0 +1,40 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++#include "exfat_data.h" ++#include "exfat_oal.h" ++ ++#include "exfat_blkdev.h" ++#include "exfat_cache.h" ++#include "exfat_nls.h" ++#include "exfat_super.h" ++#include "exfat.h" ++ ++FS_STRUCT_T fs_struct[MAX_DRIVE]; ++ ++DECLARE_MUTEX(f_sem); ++BUF_CACHE_T FAT_cache_array[FAT_CACHE_SIZE]; ++BUF_CACHE_T FAT_cache_lru_list; ++BUF_CACHE_T FAT_cache_hash_list[FAT_CACHE_HASH_SIZE]; ++ ++DECLARE_MUTEX(b_sem); ++BUF_CACHE_T buf_cache_array[BUF_CACHE_SIZE]; ++BUF_CACHE_T buf_cache_lru_list; ++BUF_CACHE_T buf_cache_hash_list[BUF_CACHE_HASH_SIZE]; +diff --git a/drivers/staging/exfat/exfat_data.h b/drivers/staging/exfat/exfat_data.h +new file mode 100644 +index 00000000..27ca835c +--- /dev/null ++++ b/drivers/staging/exfat/exfat_data.h +@@ -0,0 +1,39 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _EXFAT_DATA_H ++#define _EXFAT_DATA_H ++#include "exfat_config.h" ++#include "exfat_global.h" ++#ifdef __cplusplus ++extern "C" { ++#endif ++#define MAX_DEVICE 2 ++#define MAX_DRIVE 2 ++#define MAX_OPEN 20 ++#define MAX_DENTRY 512 ++#define FAT_CACHE_SIZE 128 ++#define FAT_CACHE_HASH_SIZE 64 ++#define BUF_CACHE_SIZE 256 ++#define BUF_CACHE_HASH_SIZE 64 ++#define DEFAULT_CODEPAGE 437 ++#define DEFAULT_IOCHARSET "utf8" ++#ifdef __cplusplus ++} ++#endif ++#endif +diff --git a/drivers/staging/exfat/exfat_global.c b/drivers/staging/exfat/exfat_global.c +new file mode 100644 +index 00000000..89934f9d +--- /dev/null ++++ b/drivers/staging/exfat/exfat_global.c +@@ -0,0 +1,119 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++ ++INT32 __wstrchr(UINT16 *str, UINT16 wchar) ++{ ++ while (*str) { ++ if (*(str++) == wchar) return(1); ++ } ++ return(0); ++} ++ ++INT32 __wstrlen(UINT16 *str) ++{ ++ INT32 length = 0; ++ ++ while (*(str++)) length++; ++ return(length); ++} ++ ++#define BITMAP_LOC(v) ((v) >> 3) ++#define BITMAP_SHIFT(v) ((v) & 0x07) ++ ++void Bitmap_set_all(UINT8 *bitmap, INT32 mapsize) ++{ ++ MEMSET(bitmap, 0xFF, mapsize); ++} ++ ++void Bitmap_clear_all(UINT8 *bitmap, INT32 mapsize) ++{ ++ MEMSET(bitmap, 0x0, mapsize); ++} ++ ++INT32 Bitmap_test(UINT8 *bitmap, INT32 i) ++{ ++ UINT8 data; ++ ++ data = bitmap[BITMAP_LOC(i)]; ++ if ((data >> BITMAP_SHIFT(i)) & 0x01) return(1); ++ return(0); ++} ++ ++void Bitmap_set(UINT8 *bitmap, INT32 i) ++{ ++ bitmap[BITMAP_LOC(i)] |= (0x01 << BITMAP_SHIFT(i)); ++} ++ ++void Bitmap_clear(UINT8 *bitmap, INT32 i) ++{ ++ bitmap[BITMAP_LOC(i)] &= ~(0x01 << BITMAP_SHIFT(i)); ++} ++ ++void Bitmap_nbits_set(UINT8 *bitmap, INT32 offset, INT32 nbits) ++{ ++ INT32 i; ++ ++ for (i = 0; i < nbits; i++) { ++ Bitmap_set(bitmap, offset+i); ++ } ++} ++ ++void Bitmap_nbits_clear(UINT8 *bitmap, INT32 offset, INT32 nbits) ++{ ++ INT32 i; ++ ++ for (i = 0; i < nbits; i++) { ++ Bitmap_clear(bitmap, offset+i); ++ } ++} ++ ++void my_itoa(INT8 *buf, INT32 v) ++{ ++ INT32 mod[10]; ++ INT32 i; ++ ++ for (i = 0; i < 10; i++) { ++ mod[i] = (v % 10); ++ v = v / 10; ++ if (v == 0) break; ++ } ++ ++ if (i == 10) ++ i--; ++ ++ for (; i >= 0; i--) { ++ *buf = (UINT8) ('0' + mod[i]); ++ buf++; ++ } ++ *buf = '\0'; ++} ++ ++INT32 my_log2(UINT32 v) ++{ ++ UINT32 bits = 0; ++ ++ while (v > 1) { ++ if (v & 0x01) return(-1); ++ v >>= 1; ++ bits++; ++ } ++ return(bits); ++} +diff --git a/drivers/staging/exfat/exfat_global.h b/drivers/staging/exfat/exfat_global.h +new file mode 100644 +index 00000000..9d87deb2 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_global.h +@@ -0,0 +1,127 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _EXFAT_GLOBAL_H ++#define _EXFAT_GLOBAL_H ++ ++#include <linux/kernel.h> ++#include <linux/mm.h> ++#include <linux/slab.h> ++#include <linux/string.h> ++#include <linux/fs.h> ++ ++#include "exfat_config.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#ifndef TRUE ++#define TRUE 1 ++#endif ++#ifndef FALSE ++#define FALSE 0 ++#endif ++#ifndef OK ++#define OK 0 ++#endif ++#ifndef FAIL ++#define FAIL 1 ++#endif ++#ifndef NULL ++#define NULL 0 ++#endif ++ ++#define MIN(a, b) (((a) < (b)) ? (a) : (b)) ++#define MAX(a, b) (((a) > (b)) ? (a) : (b)) ++ ++ typedef char INT8; ++ typedef short INT16; ++ typedef int INT32; ++ typedef long long INT64; ++ typedef unsigned char UINT8; ++ typedef unsigned short UINT16; ++ typedef unsigned int UINT32; ++ typedef unsigned long long UINT64; ++ typedef unsigned char BOOL; ++ ++#ifdef MALLOC ++#undef MALLOC ++#endif ++#ifdef FREE ++#undef FREE ++#endif ++#ifdef MEMSET ++#undef MEMSET ++#endif ++#ifdef MEMCPY ++#undef MEMCPY ++#endif ++#ifdef MEMCMP ++#undef MEMCMP ++#endif ++ ++#define MALLOC(size) kmalloc(size, GFP_KERNEL) ++#define FREE(mem) if (mem) kfree(mem) ++#define MEMSET(mem, value, size) memset(mem, value, size) ++#define MEMCPY(dest, src, size) memcpy(dest, src, size) ++#define MEMCMP(mem1, mem2, size) memcmp(mem1, mem2, size) ++#define COPY_DENTRY(dest, src) memcpy(dest, src, sizeof(DENTRY_T)) ++ ++#define STRCPY(dest, src) strcpy(dest, src) ++#define STRNCPY(dest, src, n) strncpy(dest, src, n) ++#define STRCAT(str1, str2) strcat(str1, str2) ++#define STRCMP(str1, str2) strcmp(str1, str2) ++#define STRNCMP(str1, str2, n) strncmp(str1, str2, n) ++#define STRLEN(str) strlen(str) ++ ++ INT32 __wstrchr(UINT16 *str, UINT16 wchar); ++ INT32 __wstrlen(UINT16 *str); ++ ++#define WSTRCHR(str, wchar) __wstrchr(str, wchar) ++#define WSTRLEN(str) __wstrlen(str) ++ ++#if EXFAT_CONFIG_DEBUG_MSG ++#define PRINTK(...) \ ++ do { \ ++ printk("[EXFAT] " __VA_ARGS__); \ ++ } while(0) ++#else ++#define PRINTK(...) ++#endif ++ ++ void Bitmap_set_all(UINT8 *bitmap, INT32 mapsize); ++ void Bitmap_clear_all(UINT8 *bitmap, INT32 mapsize); ++ INT32 Bitmap_test(UINT8 *bitmap, INT32 i); ++ void Bitmap_set(UINT8 *bitmap, INT32 i); ++ void Bitmap_clear(UINT8 *bitmpa, INT32 i); ++ void Bitmap_nbits_set(UINT8 *bitmap, INT32 offset, INT32 nbits); ++ void Bitmap_nbits_clear(UINT8 *bitmap, INT32 offset, INT32 nbits); ++ ++ void my_itoa(INT8 *buf, INT32 v); ++ INT32 my_log2(UINT32 v); ++ ++#ifdef PRINT ++#undef PRINT ++#endif ++ ++#define PRINT printk ++#ifdef __cplusplus ++} ++#endif ++#endif +diff --git a/drivers/staging/exfat/exfat_nls.c b/drivers/staging/exfat/exfat_nls.c +new file mode 100644 +index 00000000..04ac3998 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_nls.c +@@ -0,0 +1,347 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++#include "exfat_data.h" ++ ++#include "exfat_nls.h" ++#include "exfat_api.h" ++#include "exfat_super.h" ++#include "exfat.h" ++ ++#include <linux/nls.h> ++ ++static UINT16 bad_dos_chars[] = { ++ 0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D, ++ 0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D, ++ 0 ++}; ++ ++static UINT16 bad_uni_chars[] = { ++ 0x0022, 0x002A, 0x002F, 0x003A, ++ 0x003C, 0x003E, 0x003F, 0x005C, 0x007C, ++ 0 ++}; ++ ++static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy); ++static INT32 convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy); ++ ++UINT16 nls_upper(struct super_block *sb, UINT16 a) ++{ ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ ++ if (EXFAT_SB(sb)->options.casesensitive) ++ return(a); ++ if ((p_fs->vol_utbl)[get_col_index(a)] != NULL) ++ return (p_fs->vol_utbl)[get_col_index(a)][get_row_index(a)]; ++ else ++ return a; ++} ++ ++INT32 nls_dosname_cmp(struct super_block *sb, UINT8 *a, UINT8 *b) ++{ ++ return(STRNCMP((void *) a, (void *) b, DOS_NAME_LENGTH)); ++} ++ ++INT32 nls_uniname_cmp(struct super_block *sb, UINT16 *a, UINT16 *b) ++{ ++ INT32 i; ++ ++ for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) { ++ if (nls_upper(sb, *a) != nls_upper(sb, *b)) return(1); ++ if (*a == 0x0) return(0); ++ } ++ return(0); ++} ++ ++void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, INT32 *p_lossy) ++{ ++ INT32 i, j, len, lossy = FALSE; ++ UINT8 buf[MAX_CHARSET_SIZE]; ++ UINT8 lower = 0, upper = 0; ++ UINT8 *dosname = p_dosname->name; ++ UINT16 *uniname = p_uniname->name; ++ UINT16 *p, *last_period; ++ struct nls_table *nls = EXFAT_SB(sb)->nls_disk; ++ ++ for (i = 0; i < DOS_NAME_LENGTH; i++) { ++ *(dosname+i) = ' '; ++ } ++ ++ if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_CUR_DIR_NAME)) { ++ *(dosname) = '.'; ++ p_dosname->name_case = 0x0; ++ if (p_lossy != NULL) *p_lossy = FALSE; ++ return; ++ } ++ ++ if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_PAR_DIR_NAME)) { ++ *(dosname) = '.'; ++ *(dosname+1) = '.'; ++ p_dosname->name_case = 0x0; ++ if (p_lossy != NULL) *p_lossy = FALSE; ++ return; ++ } ++ ++ last_period = NULL; ++ for (p = uniname; *p; p++) { ++ if (*p == (UINT16) '.') last_period = p; ++ } ++ ++ i = 0; ++ while (i < DOS_NAME_LENGTH) { ++ if (i == 8) { ++ if (last_period == NULL) break; ++ ++ if (uniname <= last_period) { ++ if (uniname < last_period) lossy = TRUE; ++ uniname = last_period + 1; ++ } ++ } ++ ++ if (*uniname == (UINT16) '\0') { ++ break; ++ } else if (*uniname == (UINT16) ' ') { ++ lossy = TRUE; ++ } else if (*uniname == (UINT16) '.') { ++ if (uniname < last_period) lossy = TRUE; ++ else i = 8; ++ } else if (WSTRCHR(bad_dos_chars, *uniname)) { ++ lossy = TRUE; ++ *(dosname+i) = '_'; ++ i++; ++ } else { ++ len = convert_uni_to_ch(nls, buf, *uniname, &lossy); ++ ++ if (len > 1) { ++ if ((i >= 8) && ((i+len) > DOS_NAME_LENGTH)) { ++ break; ++ } ++ if ((i < 8) && ((i+len) > 8)) { ++ i = 8; ++ continue; ++ } ++ ++ lower = 0xFF; ++ ++ for (j = 0; j < len; j++, i++) { ++ *(dosname+i) = *(buf+j); ++ } ++ } else { ++ if ((*buf >= 'a') && (*buf <= 'z')) { ++ *(dosname+i) = *buf - ('a' - 'A'); ++ ++ if (i < 8) lower |= 0x08; ++ else lower |= 0x10; ++ } else if ((*buf >= 'A') && (*buf <= 'Z')) { ++ *(dosname+i) = *buf; ++ ++ if (i < 8) upper |= 0x08; ++ else upper |= 0x10; ++ } else { ++ *(dosname+i) = *buf; ++ } ++ i++; ++ } ++ } ++ ++ uniname++; ++ } ++ ++ if (*dosname == 0xE5) *dosname = 0x05; ++ if (*uniname != 0x0) lossy = TRUE; ++ ++ if (upper & lower) p_dosname->name_case = 0xFF; ++ else p_dosname->name_case = lower; ++ ++ if (p_lossy != NULL) *p_lossy = lossy; ++} ++ ++void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) ++{ ++ INT32 i = 0, j, n = 0; ++ UINT8 buf[DOS_NAME_LENGTH+2]; ++ UINT8 *dosname = p_dosname->name; ++ UINT16 *uniname = p_uniname->name; ++ struct nls_table *nls = EXFAT_SB(sb)->nls_disk; ++ ++ if (*dosname == 0x05) { ++ *buf = 0xE5; ++ i++; ++ n++; ++ } ++ ++ for ( ; i < 8; i++, n++) { ++ if (*(dosname+i) == ' ') break; ++ ++ if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x08)) ++ *(buf+n) = *(dosname+i) + ('a' - 'A'); ++ else ++ *(buf+n) = *(dosname+i); ++ } ++ if (*(dosname+8) != ' ') { ++ *(buf+n) = '.'; ++ n++; ++ } ++ ++ for (i = 8; i < DOS_NAME_LENGTH; i++, n++) { ++ if (*(dosname+i) == ' ') break; ++ ++ if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x10)) ++ *(buf+n) = *(dosname+i) + ('a' - 'A'); ++ else ++ *(buf+n) = *(dosname+i); ++ } ++ *(buf+n) = '\0'; ++ ++ i = j = 0; ++ while (j < (MAX_NAME_LENGTH-1)) { ++ if (*(buf+i) == '\0') break; ++ ++ i += convert_ch_to_uni(nls, uniname, (buf+i), NULL); ++ ++ uniname++; ++ j++; ++ } ++ ++ *uniname = (UINT16) '\0'; ++} ++ ++void nls_uniname_to_cstring(struct super_block *sb, UINT8 *p_cstring, UNI_NAME_T *p_uniname) ++{ ++ INT32 i, j, len; ++ UINT8 buf[MAX_CHARSET_SIZE]; ++ UINT16 *uniname = p_uniname->name; ++ struct nls_table *nls = EXFAT_SB(sb)->nls_io; ++ ++ i = 0; ++ while (i < (MAX_NAME_LENGTH-1)) { ++ if (*uniname == (UINT16) '\0') break; ++ ++ len = convert_uni_to_ch(nls, buf, *uniname, NULL); ++ ++ if (len > 1) { ++ for (j = 0; j < len; j++) ++ *p_cstring++ = (INT8) *(buf+j); ++ } else { ++ *p_cstring++ = (INT8) *buf; ++ } ++ ++ uniname++; ++ i++; ++ } ++ ++ *p_cstring = '\0'; ++} ++ ++void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, UINT8 *p_cstring, INT32 *p_lossy) ++{ ++ INT32 i, j, lossy = FALSE; ++ UINT8 *end_of_name; ++ UINT16 upname[MAX_NAME_LENGTH]; ++ UINT16 *uniname = p_uniname->name; ++ struct nls_table *nls = EXFAT_SB(sb)->nls_io; ++ ++ end_of_name = p_cstring + STRLEN((INT8 *) p_cstring); ++ ++ while (*(--end_of_name) == ' ') { ++ if (end_of_name < p_cstring) break; ++ } ++ *(++end_of_name) = '\0'; ++ ++ if (STRCMP((INT8 *) p_cstring, ".") && STRCMP((INT8 *) p_cstring, "..")) { ++ while (*(--end_of_name) == '.') { ++ if (end_of_name < p_cstring) break; ++ } ++ *(++end_of_name) = '\0'; ++ } ++ ++ if (*p_cstring == '\0') ++ lossy = TRUE; ++ ++ i = j = 0; ++ while (j < (MAX_NAME_LENGTH-1)) { ++ if (*(p_cstring+i) == '\0') break; ++ ++ i += convert_ch_to_uni(nls, uniname, (UINT8 *)(p_cstring+i), &lossy); ++ ++ if ((*uniname < 0x0020) || WSTRCHR(bad_uni_chars, *uniname)) ++ lossy = TRUE; ++ ++ *(upname+j) = nls_upper(sb, *uniname); ++ ++ uniname++; ++ j++; ++ } ++ ++ if (*(p_cstring+i) != '\0') ++ lossy = TRUE; ++ *uniname = (UINT16) '\0'; ++ ++ p_uniname->name_len = j; ++ p_uniname->name_hash = calc_checksum_2byte((void *) upname, j<<1, 0, CS_DEFAULT); ++ ++ if (p_lossy != NULL) ++ *p_lossy = lossy; ++} ++ ++static INT32 convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy) ++{ ++ int len; ++ ++ *uni = 0x0; ++ ++ if (ch[0] < 0x80) { ++ *uni = (UINT16) ch[0]; ++ return(1); ++ } ++ ++ if ((len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni)) < 0) { ++ printk("%s: fail to use nls \n", __func__); ++ if (lossy != NULL) ++ *lossy = TRUE; ++ *uni = (UINT16) '_'; ++ if (!strcmp(nls->charset, "utf8")) return(1); ++ else return(2); ++ } ++ ++ return(len); ++} ++ ++static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy) ++{ ++ int len; ++ ++ ch[0] = 0x0; ++ ++ if (uni < 0x0080) { ++ ch[0] = (UINT8) uni; ++ return(1); ++ } ++ ++ if ((len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE)) < 0) { ++ printk("%s: fail to use nls \n", __func__); ++ if (lossy != NULL) *lossy = TRUE; ++ ch[0] = '_'; ++ return(1); ++ } ++ ++ return(len); ++ ++} +diff --git a/drivers/staging/exfat/exfat_nls.h b/drivers/staging/exfat/exfat_nls.h +new file mode 100644 +index 00000000..8a01d269 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_nls.h +@@ -0,0 +1,68 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _EXFAT_NLS_H ++#define _EXFAT_NLS_H ++ ++#include <linux/nls.h> ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++#include "exfat_api.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define NUM_UPCASE 2918 ++ ++#define DOS_CUR_DIR_NAME ". " ++#define DOS_PAR_DIR_NAME ".. " ++ ++#if (FFS_CONFIG_LITTLE_ENDIAN == 1) ++#define UNI_CUR_DIR_NAME ".\0" ++#define UNI_PAR_DIR_NAME ".\0.\0" ++#else ++#define UNI_CUR_DIR_NAME "\0." ++#define UNI_PAR_DIR_NAME "\0.\0." ++#endif ++ ++ ++typedef struct { ++ UINT8 name[DOS_NAME_LENGTH]; ++ UINT8 name_case; ++} DOS_NAME_T; ++ ++typedef struct { ++ UINT16 name[MAX_NAME_LENGTH]; ++ UINT16 name_hash; ++ UINT8 name_len; ++} UNI_NAME_T; ++ ++UINT16 nls_upper(struct super_block *sb, UINT16 a); ++INT32 nls_dosname_cmp(struct super_block *sb, UINT8 *a, UINT8 *b); ++INT32 nls_uniname_cmp(struct super_block *sb, UINT16 *a, UINT16 *b); ++void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, INT32 *p_lossy); ++void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); ++void nls_uniname_to_cstring(struct super_block *sb, UINT8 *p_cstring, UNI_NAME_T *p_uniname); ++void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, UINT8 *p_cstring, INT32 *p_lossy); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif +diff --git a/drivers/staging/exfat/exfat_oal.c b/drivers/staging/exfat/exfat_oal.c +new file mode 100644 +index 00000000..717e4bf8 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_oal.c +@@ -0,0 +1,146 @@ ++/* Some of the source code in this file came from "linux/fs/fat/misc.c". */ ++/* ++ * linux/fs/fat/misc.c ++ * ++ * Written 1992,1993 by Werner Almesberger ++ * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 ++ * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) ++ */ ++ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include <linux/semaphore.h> ++#include <linux/time.h> ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++#include "exfat_api.h" ++#include "exfat_oal.h" ++ ++DECLARE_MUTEX(z_sem); ++ ++INT32 sm_init(struct semaphore *sm) ++{ ++ sema_init(sm, 1); ++ return(0); ++} ++ ++INT32 sm_P(struct semaphore *sm) ++{ ++ down(sm); ++ return 0; ++} ++ ++void sm_V(struct semaphore *sm) ++{ ++ up(sm); ++} ++ ++extern struct timezone sys_tz; ++ ++#define UNIX_SECS_1980 315532800L ++ ++#if BITS_PER_LONG == 64 ++#define UNIX_SECS_2108 4354819200L ++#endif ++ ++#define DAYS_DELTA_DECADE (365 * 10 + 2) ++#define NO_LEAP_YEAR_2100 (120) ++#define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != NO_LEAP_YEAR_2100) ++ ++#define SECS_PER_MIN (60) ++#define SECS_PER_HOUR (60 * SECS_PER_MIN) ++#define SECS_PER_DAY (24 * SECS_PER_HOUR) ++ ++#define MAKE_LEAP_YEAR(leap_year, year) \ ++ do { \ ++ if (unlikely(year > NO_LEAP_YEAR_2100)) \ ++ leap_year = ((year + 3) / 4) - 1; \ ++ else \ ++ leap_year = ((year + 3) / 4); \ ++ } while(0) ++ ++ ++ ++static time_t accum_days_in_year[] = { ++ 0, 0, 31, 59, 90,120,151,181,212,243,273,304,334, 0, 0, 0, ++}; ++ ++ ++TIMESTAMP_T *tm_current(TIMESTAMP_T *tp) ++{ ++ struct timespec ts = CURRENT_TIME_SEC; ++ time_t second = ts.tv_sec; ++ time_t day, leap_day, month, year; ++ ++ second -= sys_tz.tz_minuteswest * SECS_PER_MIN; ++ ++ if (second < UNIX_SECS_1980) { ++ tp->sec = 0; ++ tp->min = 0; ++ tp->hour = 0; ++ tp->day = 1; ++ tp->mon = 1; ++ tp->year = 0; ++ return(tp); ++ } ++#if BITS_PER_LONG == 64 ++ if (second >= UNIX_SECS_2108) { ++ tp->sec = 59; ++ tp->min = 59; ++ tp->hour = 23; ++ tp->day = 31; ++ tp->mon = 12; ++ tp->year = 127; ++ return(tp); ++ } ++#endif ++ ++ day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; ++ year = day / 365; ++ ++ MAKE_LEAP_YEAR(leap_day, year); ++ if (year * 365 + leap_day > day) ++ year--; ++ ++ MAKE_LEAP_YEAR(leap_day, year); ++ ++ day -= year * 365 + leap_day; ++ ++ if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { ++ month = 2; ++ } else { ++ if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) ++ day--; ++ for (month = 1; month < 12; month++) { ++ if (accum_days_in_year[month + 1] > day) ++ break; ++ } ++ } ++ day -= accum_days_in_year[month]; ++ ++ tp->sec = second % SECS_PER_MIN; ++ tp->min = (second / SECS_PER_MIN) % 60; ++ tp->hour = (second / SECS_PER_HOUR) % 24; ++ tp->day = day + 1; ++ tp->mon = month; ++ tp->year = year; ++ ++ return(tp); ++} +diff --git a/drivers/staging/exfat/exfat_oal.h b/drivers/staging/exfat/exfat_oal.h +new file mode 100644 +index 00000000..4c7d95b2 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_oal.h +@@ -0,0 +1,53 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _EXFAT_OAL_H ++#define _EXFAT_OAL_H ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++#include <linux/version.h> ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ typedef struct { ++ UINT16 sec; ++ UINT16 min; ++ UINT16 hour; ++ UINT16 day; ++ UINT16 mon; ++ UINT16 year; ++ } TIMESTAMP_T; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) ++#define DECLARE_MUTEX(m) DEFINE_SEMAPHORE(m) ++#endif ++ ++ INT32 sm_init(struct semaphore *sm); ++ INT32 sm_P(struct semaphore *sm); ++ void sm_V(struct semaphore *sm); ++ ++ TIMESTAMP_T *tm_current(TIMESTAMP_T *tm); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/drivers/staging/exfat/exfat_part.h b/drivers/staging/exfat/exfat_part.h +new file mode 100644 +index 00000000..cc6db65c +--- /dev/null ++++ b/drivers/staging/exfat/exfat_part.h +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _EXFAT_PART_H ++#define _EXFAT_PART_H ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++#include "exfat_api.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define MBR_SIGNATURE 0xAA55 ++ typedef struct { ++ UINT8 boot_code[446]; ++ UINT8 partition[64]; ++ UINT8 signature[2]; ++ } MBR_SECTOR_T; ++ ++ typedef struct { ++ UINT8 def_boot; ++ UINT8 bgn_chs[3]; ++ UINT8 sys_type; ++ UINT8 end_chs[3]; ++ UINT8 start_sector[4]; ++ UINT8 num_sectors[4]; ++ } PART_ENTRY_T; ++ ++ INT32 ffsSetPartition(INT32 dev, INT32 num_vol, PART_INFO_T *vol_spec); ++ INT32 ffsGetPartition(INT32 dev, INT32 *num_vol, PART_INFO_T *vol_spec); ++ INT32 ffsGetDevInfo(INT32 dev, DEV_INFO_T *info); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c +new file mode 100644 +index 00000000..8c937631 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_super.c +@@ -0,0 +1,2154 @@ ++/* Some of the source code in this file came from "linux/fs/fat/file.c","linux/fs/fat/inode.c" and "linux/fs/fat/misc.c". */ ++/* ++ * linux/fs/fat/file.c ++ * ++ * Written 1992,1993 by Werner Almesberger ++ * ++ * regular file handling primitives for fat-based filesystems ++ */ ++ ++/* ++ * linux/fs/fat/inode.c ++ * ++ * Written 1992,1993 by Werner Almesberger ++ * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner ++ * Rewritten for the constant inumbers support by Al Viro ++ * ++ * Fixes: ++ * ++ * Max Cohan: Fixed invalid FSINFO offset when info_sector is 0 ++ */ ++ ++/* ++ * linux/fs/fat/misc.c ++ * ++ * Written 1992,1993 by Werner Almesberger ++ * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 ++ * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) ++ */ ++ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include <linux/version.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/time.h> ++#include <linux/slab.h> ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++#include <linux/smp_lock.h> ++#endif ++#include <linux/seq_file.h> ++#include <linux/pagemap.h> ++#include <linux/mpage.h> ++#include <linux/buffer_head.h> ++#include <linux/exportfs.h> ++#include <linux/mount.h> ++#include <linux/vfs.h> ++#include <linux/parser.h> ++#include <linux/uio.h> ++#include <linux/writeback.h> ++#include <linux/log2.h> ++#include <linux/hash.h> ++#include <linux/backing-dev.h> ++#include <linux/sched.h> ++#include <linux/fs_struct.h> ++#include <linux/namei.h> ++#include <asm/current.h> ++#include <asm/unaligned.h> ++ ++#include "exfat_version.h" ++#include "exfat_config.h" ++#include "exfat_global.h" ++#include "exfat_data.h" ++#include "exfat_oal.h" ++ ++#include "exfat_blkdev.h" ++#include "exfat_cache.h" ++#include "exfat_part.h" ++#include "exfat_nls.h" ++#include "exfat_api.h" ++#include "exfat.h" ++ ++#include "exfat_super.h" ++ ++static struct kmem_cache *exfat_inode_cachep; ++ ++static int exfat_default_codepage = DEFAULT_CODEPAGE; ++static char exfat_default_iocharset[] = DEFAULT_IOCHARSET; ++ ++extern struct timezone sys_tz; ++ ++#define CHECK_ERR(x) BUG_ON(x) ++#define ELAPSED_TIME 0 ++ ++#if (ELAPSED_TIME == 1) ++#include <linux/time.h> ++ ++static UINT32 __t1, __t2; ++static UINT32 get_current_msec(void) ++{ ++ struct timeval tm; ++ do_gettimeofday(&tm); ++ return((UINT32)(tm.tv_sec*1000000 + tm.tv_usec)); ++} ++#define TIME_START() do {__t1 = get_current_msec();} while (0) ++#define TIME_END() do {__t2 = get_current_msec();} while (0) ++#define PRINT_TIME(n) do {printk("[EXFAT] Elapsed time %d = %d (usec)\n", n, (__t2 - __t1));} while (0) ++#else ++#define TIME_START() ++#define TIME_END() ++#define PRINT_TIME(n) ++#endif ++ ++#define UNIX_SECS_1980 315532800L ++ ++#if BITS_PER_LONG == 64 ++#define UNIX_SECS_2108 4354819200L ++#endif ++#define DAYS_DELTA_DECADE (365 * 10 + 2) ++#define NO_LEAP_YEAR_2100 (120) ++#define IS_LEAP_YEAR(y) (!((y) & 0x3) && (y) != NO_LEAP_YEAR_2100) ++ ++#define SECS_PER_MIN (60) ++#define SECS_PER_HOUR (60 * SECS_PER_MIN) ++#define SECS_PER_DAY (24 * SECS_PER_HOUR) ++ ++#define MAKE_LEAP_YEAR(leap_year, year) \ ++ do { \ ++ if (unlikely(year > NO_LEAP_YEAR_2100)) \ ++ leap_year = ((year + 3) / 4) - 1; \ ++ else \ ++ leap_year = ((year + 3) / 4); \ ++ } while(0) ++ ++static time_t accum_days_in_year[] = { ++ 0, 0, 31, 59, 90,120,151,181,212,243,273,304,334, 0, 0, 0, ++}; ++ ++static void _exfat_truncate(struct inode *inode, loff_t old_size); ++ ++void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec *ts, ++ DATE_TIME_T *tp) ++{ ++ time_t year = tp->Year; ++ time_t ld; ++ ++ MAKE_LEAP_YEAR(ld, year); ++ ++ if (IS_LEAP_YEAR(year) && (tp->Month) > 2) ++ ld++; ++ ++ ts->tv_sec = tp->Second + tp->Minute * SECS_PER_MIN ++ + tp->Hour * SECS_PER_HOUR ++ + (year * 365 + ld + accum_days_in_year[(tp->Month)] + (tp->Day - 1) + DAYS_DELTA_DECADE) * SECS_PER_DAY ++ + sys_tz.tz_minuteswest * SECS_PER_MIN; ++ ts->tv_nsec = 0; ++} ++ ++void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec *ts, ++ DATE_TIME_T *tp) ++{ ++ time_t second = ts->tv_sec; ++ time_t day, month, year; ++ time_t ld; ++ ++ second -= sys_tz.tz_minuteswest * SECS_PER_MIN; ++ ++ if (second < UNIX_SECS_1980) { ++ tp->Second = 0; ++ tp->Minute = 0; ++ tp->Hour = 0; ++ tp->Day = 1; ++ tp->Month = 1; ++ tp->Year = 0; ++ return; ++ } ++#if (BITS_PER_LONG == 64) ++ if (second >= UNIX_SECS_2108) { ++ tp->Second = 59; ++ tp->Minute = 59; ++ tp->Hour = 23; ++ tp->Day = 31; ++ tp->Month = 12; ++ tp->Year = 127; ++ return; ++ } ++#endif ++ day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; ++ year = day / 365; ++ MAKE_LEAP_YEAR(ld, year); ++ if (year * 365 + ld > day) ++ year--; ++ ++ MAKE_LEAP_YEAR(ld, year); ++ day -= year * 365 + ld; ++ ++ if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { ++ month = 2; ++ } else { ++ if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) ++ day--; ++ for (month = 1; month < 12; month++) { ++ if (accum_days_in_year[month + 1] > day) ++ break; ++ } ++ } ++ day -= accum_days_in_year[month]; ++ ++ tp->Second = second % SECS_PER_MIN; ++ tp->Minute = (second / SECS_PER_MIN) % 60; ++ tp->Hour = (second / SECS_PER_HOUR) % 24; ++ tp->Day = day + 1; ++ tp->Month = month; ++ tp->Year = year; ++} ++ ++static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ++static int exfat_generic_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); ++#else ++static long exfat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); ++#endif ++static int exfat_sync_inode(struct inode *inode); ++static struct inode *exfat_build_inode(struct super_block *sb, FILE_ID_T *fid, loff_t i_pos); ++static void exfat_detach(struct inode *inode); ++static void exfat_attach(struct inode *inode, loff_t i_pos); ++static inline unsigned long exfat_hash(loff_t i_pos); ++static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc); ++static void exfat_write_super(struct super_block *sb); ++ ++static void __lock_super(struct super_block *sb) ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) ++ lock_super(sb); ++#else ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ mutex_lock(&sbi->s_lock); ++#endif ++} ++ ++static void __unlock_super(struct super_block *sb) ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) ++ unlock_super(sb); ++#else ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ mutex_unlock(&sbi->s_lock); ++#endif ++} ++ ++static int __is_sb_dirty(struct super_block *sb) ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) ++ return sb->s_dirt; ++#else ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ return sbi->s_dirt; ++#endif ++} ++ ++static void __set_sb_clean(struct super_block *sb) ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) ++ sb->s_dirt = 0; ++#else ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ sbi->s_dirt = 0; ++#endif ++} ++ ++static int exfat_readdir(struct file *filp, void *dirent, filldir_t filldir) ++{ ++ struct inode *inode = filp->f_path.dentry->d_inode; ++ struct super_block *sb = inode->i_sb; ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ FS_INFO_T *p_fs = &(sbi->fs_info); ++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); ++ DIR_ENTRY_T de; ++ unsigned long inum; ++ loff_t cpos; ++ int err = 0; ++ ++ __lock_super(sb); ++ ++ cpos = filp->f_pos; ++ if ((p_fs->vol_type == EXFAT) || (inode->i_ino == EXFAT_ROOT_INO)) { ++ while (cpos < 2) { ++ if (inode->i_ino == EXFAT_ROOT_INO) ++ inum = EXFAT_ROOT_INO; ++ else if (cpos == 0) ++ inum = inode->i_ino; ++ else ++ inum = parent_ino(filp->f_path.dentry); ++ ++ if (filldir(dirent, "..", cpos+1, cpos, inum, DT_DIR) < 0) ++ goto out; ++ cpos++; ++ filp->f_pos++; ++ } ++ if (cpos == 2) { ++ cpos = 0; ++ } ++ } ++ if (cpos & (DENTRY_SIZE - 1)) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++get_new: ++ EXFAT_I(inode)->fid.size = i_size_read(inode); ++ EXFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS; ++ ++ err = FsReadDir(inode, &de); ++ if (err) { ++ if (err == FFS_MEDIAERR) { ++ cpos += 1 << p_bd->sector_size_bits; ++ cpos &= ~((1 << p_bd->sector_size_bits)-1); ++ } ++ ++ err = -EIO; ++ goto end_of_dir; ++ } ++ ++ cpos = EXFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS; ++ ++ if (!de.Name[0]) ++ goto end_of_dir; ++ ++ if (!memcmp(de.ShortName, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { ++ inum = inode->i_ino; ++ } else if (!memcmp(de.ShortName, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { ++ inum = parent_ino(filp->f_path.dentry); ++ } else { ++ loff_t i_pos = ((loff_t) EXFAT_I(inode)->fid.start_clu << 32) | ++ ((EXFAT_I(inode)->fid.rwoffset-1) & 0xffffffff); ++ ++ struct inode *tmp = exfat_iget(sb, i_pos); ++ if (tmp) { ++ inum = tmp->i_ino; ++ iput(tmp); ++ } else { ++ inum = iunique(sb, EXFAT_ROOT_INO); ++ } ++ } ++ ++ if (filldir(dirent, de.Name, strlen(de.Name), cpos-1, inum, ++ (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG) < 0) ++ goto out; ++ ++ filp->f_pos = cpos; ++ goto get_new; ++ ++end_of_dir: ++ filp->f_pos = cpos; ++out: ++ __unlock_super(sb); ++ return err; ++} ++ ++static int exfat_ioctl_volume_id(struct inode *dir) ++{ ++ struct super_block *sb = dir->i_sb; ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ FS_INFO_T *p_fs = &(sbi->fs_info); ++ ++ return p_fs->vol_id; ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ++static int exfat_generic_ioctl(struct inode *inode, struct file *filp, ++ unsigned int cmd, unsigned long arg) ++#else ++static long exfat_generic_ioctl(struct file *filp, ++ unsigned int cmd, unsigned long arg) ++#endif ++{ ++#if EXFAT_CONFIG_KERNEL_DEBUG ++#if !(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) ++ struct inode *inode = filp->f_dentry->d_inode; ++#endif ++ unsigned int flags; ++#endif ++ ++ switch (cmd) { ++ case EXFAT_IOCTL_GET_VOLUME_ID: ++ return exfat_ioctl_volume_id(inode); ++#if EXFAT_CONFIG_KERNEL_DEBUG ++ case EXFAT_IOC_GET_DEBUGFLAGS: { ++ struct super_block *sb = inode->i_sb; ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ ++ flags = sbi->debug_flags; ++ return put_user(flags, (int __user *)arg); ++ } ++ case EXFAT_IOC_SET_DEBUGFLAGS: { ++ struct super_block *sb = inode->i_sb; ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ if (get_user(flags, (int __user *) arg)) ++ return -EFAULT; ++ ++ __lock_super(sb); ++ sbi->debug_flags = flags; ++ __unlock_super(sb); ++ ++ return 0; ++ } ++#endif ++ default: ++ return -ENOTTY; ++ } ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ++static int exfat_file_fsync(struct file *filp, int datasync) ++{ ++ struct inode *inode = filp->f_mapping->host; ++ struct super_block *sb = inode->i_sb; ++ int res, err; ++ ++ res = generic_file_fsync(filp, datasync); ++ err = FsSyncVol(sb, 1); ++ ++ return res ? res : err; ++} ++#endif ++ ++const struct file_operations exfat_dir_operations = { ++ .llseek = generic_file_llseek, ++ .read = generic_read_dir, ++ .readdir = exfat_readdir, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ++ .ioctl = exfat_generic_ioctl, ++ .fsync = exfat_file_fsync, ++#else ++ .unlocked_ioctl = exfat_generic_ioctl, ++ .fsync = generic_file_fsync, ++#endif ++}; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) ++static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ bool excl) ++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) ++static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ struct nameidata *nd) ++#else ++static int exfat_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *nd) ++#endif ++{ ++ struct super_block *sb = dir->i_sb; ++ struct inode *inode; ++ struct timespec ts; ++ FILE_ID_T fid; ++ loff_t i_pos; ++ int err; ++ ++ __lock_super(sb); ++ ++ PRINTK("exfat_create entered\n"); ++ ++ ts = CURRENT_TIME_SEC; ++ ++ err = FsCreateFile(dir, (UINT8 *) dentry->d_name.name, FM_REGULAR, &fid); ++ if (err) { ++ if (err == FFS_INVALIDPATH) ++ err = -EINVAL; ++ else if (err == FFS_FILEEXIST) ++ err = -EEXIST; ++ else if (err == FFS_FULL) ++ err = -ENOSPC; ++ else if (err == FFS_NAMETOOLONG) ++ err = -ENAMETOOLONG; ++ else ++ err = -EIO; ++ goto out; ++ } ++ dir->i_version++; ++ dir->i_ctime = dir->i_mtime = dir->i_atime = ts; ++ if (IS_DIRSYNC(dir)) ++ (void) exfat_sync_inode(dir); ++ else ++ mark_inode_dirty(dir); ++ ++ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); ++ ++ inode = exfat_build_inode(sb, &fid, i_pos); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ goto out; ++ } ++ inode->i_version++; ++ inode->i_mtime = inode->i_atime = inode->i_ctime = ts; ++ ++ dentry->d_time = dentry->d_parent->d_inode->i_version; ++ d_instantiate(dentry, inode); ++ ++out: ++ __unlock_super(sb); ++ PRINTK("exfat_create exited\n"); ++ return err; ++} ++ ++static int exfat_find(struct inode *dir, struct qstr *qname, ++ FILE_ID_T *fid) ++{ ++ int err; ++ ++ if (qname->len == 0) ++ return -ENOENT; ++ ++ err = FsLookupFile(dir, (UINT8 *) qname->name, fid); ++ if (err) ++ return -ENOENT; ++ ++ return 0; ++} ++ ++static int exfat_d_anon_disconn(struct dentry *dentry) ++{ ++ return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED); ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) ++static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, ++ unsigned int flags) ++#else ++static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *nd) ++#endif ++{ ++ struct super_block *sb = dir->i_sb; ++ struct inode *inode; ++ struct dentry *alias; ++ int err; ++ FILE_ID_T fid; ++ loff_t i_pos; ++ UINT64 ret; ++ mode_t i_mode; ++ ++ __lock_super(sb); ++ PRINTK("exfat_lookup entered\n"); ++ err = exfat_find(dir, &dentry->d_name, &fid); ++ if (err) { ++ if (err == -ENOENT) { ++ inode = NULL; ++ goto out; ++ } ++ goto error; ++ } ++ ++ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); ++ inode = exfat_build_inode(sb, &fid, i_pos); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ goto error; ++ } ++ ++ i_mode = inode->i_mode; ++ if (S_ISLNK(i_mode)) { ++ EXFAT_I(inode)->target = MALLOC(i_size_read(inode)+1); ++ if (!EXFAT_I(inode)->target) { ++ err = -ENOMEM; ++ goto error; ++ } ++ FsReadFile(dir, &fid, EXFAT_I(inode)->target, i_size_read(inode), &ret); ++ *(EXFAT_I(inode)->target + i_size_read(inode)) = '\0'; ++ } ++ ++ alias = d_find_alias(inode); ++ if (alias && !exfat_d_anon_disconn(alias)) { ++ CHECK_ERR(d_unhashed(alias)); ++ if (!S_ISDIR(i_mode)) ++ d_move(alias, dentry); ++ iput(inode); ++ __unlock_super(sb); ++ PRINTK("exfat_lookup exited 1\n"); ++ return alias; ++ } else { ++ dput(alias); ++ } ++out: ++ __unlock_super(sb); ++ dentry->d_time = dentry->d_parent->d_inode->i_version; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) ++ dentry->d_op = sb->s_root->d_op; ++ dentry = d_splice_alias(inode, dentry); ++ if (dentry) { ++ dentry->d_op = sb->s_root->d_op; ++ dentry->d_time = dentry->d_parent->d_inode->i_version; ++ } ++#else ++ dentry = d_splice_alias(inode, dentry); ++ if (dentry) ++ dentry->d_time = dentry->d_parent->d_inode->i_version; ++#endif ++ PRINTK("exfat_lookup exited 2\n"); ++ return dentry; ++ ++error: ++ __unlock_super(sb); ++ PRINTK("exfat_lookup exited 3\n"); ++ return ERR_PTR(err); ++} ++ ++static int exfat_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ struct inode *inode = dentry->d_inode; ++ struct super_block *sb = dir->i_sb; ++ struct timespec ts; ++ int err; ++ ++ __lock_super(sb); ++ ++ PRINTK("exfat_unlink entered\n"); ++ ++ ts = CURRENT_TIME_SEC; ++ ++ EXFAT_I(inode)->fid.size = i_size_read(inode); ++ ++ err = FsRemoveFile(dir, &(EXFAT_I(inode)->fid)); ++ if (err) { ++ if (err == FFS_PERMISSIONERR) ++ err = -EPERM; ++ else ++ err = -EIO; ++ goto out; ++ } ++ dir->i_version++; ++ dir->i_mtime = dir->i_atime = ts; ++ if (IS_DIRSYNC(dir)) ++ (void) exfat_sync_inode(dir); ++ else ++ mark_inode_dirty(dir); ++ ++ clear_nlink(inode); ++ inode->i_mtime = inode->i_atime = ts; ++ exfat_detach(inode); ++ remove_inode_hash(inode); ++ ++out: ++ __unlock_super(sb); ++ PRINTK("exfat_unlink exited\n"); ++ return err; ++} ++ ++static int exfat_symlink(struct inode *dir, struct dentry *dentry, const char *target) ++{ ++ struct super_block *sb = dir->i_sb; ++ struct inode *inode; ++ struct timespec ts; ++ FILE_ID_T fid; ++ loff_t i_pos; ++ int err; ++ UINT64 len = (UINT64) strlen(target); ++ UINT64 ret; ++ ++ __lock_super(sb); ++ ++ PRINTK("exfat_symlink entered\n"); ++ ++ ts = CURRENT_TIME_SEC; ++ ++ err = FsCreateFile(dir, (UINT8 *) dentry->d_name.name, FM_SYMLINK, &fid); ++ if (err) { ++ if (err == FFS_INVALIDPATH) ++ err = -EINVAL; ++ else if (err == FFS_FILEEXIST) ++ err = -EEXIST; ++ else if (err == FFS_FULL) ++ err = -ENOSPC; ++ else ++ err = -EIO; ++ goto out; ++ } ++ ++ err = FsWriteFile(dir, &fid, (char *) target, len, &ret); ++ ++ if (err) { ++ FsRemoveFile(dir, &fid); ++ ++ if (err == FFS_FULL) ++ err = -ENOSPC; ++ else ++ err = -EIO; ++ goto out; ++ } ++ ++ dir->i_version++; ++ dir->i_ctime = dir->i_mtime = dir->i_atime = ts; ++ if (IS_DIRSYNC(dir)) ++ (void) exfat_sync_inode(dir); ++ else ++ mark_inode_dirty(dir); ++ ++ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); ++ ++ inode = exfat_build_inode(sb, &fid, i_pos); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ goto out; ++ } ++ inode->i_version++; ++ inode->i_mtime = inode->i_atime = inode->i_ctime = ts; ++ ++ EXFAT_I(inode)->target = MALLOC(len+1); ++ if (!EXFAT_I(inode)->target) { ++ err = -ENOMEM; ++ goto out; ++ } ++ MEMCPY(EXFAT_I(inode)->target, target, len+1); ++ ++ dentry->d_time = dentry->d_parent->d_inode->i_version; ++ d_instantiate(dentry, inode); ++ ++out: ++ __unlock_super(sb); ++ PRINTK("exfat_symlink exited\n"); ++ return err; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) ++static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ++#else ++static int exfat_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ struct super_block *sb = dir->i_sb; ++ struct inode *inode; ++ struct timespec ts; ++ FILE_ID_T fid; ++ loff_t i_pos; ++ int err; ++ ++ __lock_super(sb); ++ ++ PRINTK("exfat_mkdir entered\n"); ++ ++ ts = CURRENT_TIME_SEC; ++ ++ err = FsCreateDir(dir, (UINT8 *) dentry->d_name.name, &fid); ++ if (err) { ++ if (err == FFS_INVALIDPATH) ++ err = -EINVAL; ++ else if (err == FFS_FILEEXIST) ++ err = -EEXIST; ++ else if (err == FFS_FULL) ++ err = -ENOSPC; ++ else if (err == FFS_NAMETOOLONG) ++ err = -ENAMETOOLONG; ++ else ++ err = -EIO; ++ goto out; ++ } ++ dir->i_version++; ++ dir->i_ctime = dir->i_mtime = dir->i_atime = ts; ++ if (IS_DIRSYNC(dir)) ++ (void) exfat_sync_inode(dir); ++ else ++ mark_inode_dirty(dir); ++ inc_nlink(dir); ++ ++ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); ++ ++ inode = exfat_build_inode(sb, &fid, i_pos); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ goto out; ++ } ++ inode->i_version++; ++ inode->i_mtime = inode->i_atime = inode->i_ctime = ts; ++ ++ dentry->d_time = dentry->d_parent->d_inode->i_version; ++ d_instantiate(dentry, inode); ++ ++out: ++ __unlock_super(sb); ++ PRINTK("exfat_mkdir exited\n"); ++ return err; ++} ++ ++static int exfat_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ struct inode *inode = dentry->d_inode; ++ struct super_block *sb = dir->i_sb; ++ struct timespec ts; ++ int err; ++ ++ __lock_super(sb); ++ ++ PRINTK("exfat_rmdir entered\n"); ++ ++ ts = CURRENT_TIME_SEC; ++ ++ EXFAT_I(inode)->fid.size = i_size_read(inode); ++ ++ err = FsRemoveDir(dir, &(EXFAT_I(inode)->fid)); ++ if (err) { ++ if (err == FFS_INVALIDPATH) ++ err = -EINVAL; ++ else if (err == FFS_FILEEXIST) ++ err = -ENOTEMPTY; ++ else if (err == FFS_NOTFOUND) ++ err = -ENOENT; ++ else if (err == FFS_DIRBUSY) ++ err = -EBUSY; ++ else ++ err = -EIO; ++ goto out; ++ } ++ dir->i_version++; ++ dir->i_mtime = dir->i_atime = ts; ++ if (IS_DIRSYNC(dir)) ++ (void) exfat_sync_inode(dir); ++ else ++ mark_inode_dirty(dir); ++ drop_nlink(dir); ++ ++ clear_nlink(inode); ++ inode->i_mtime = inode->i_atime = ts; ++ exfat_detach(inode); ++ remove_inode_hash(inode); ++ ++out: ++ __unlock_super(sb); ++ PRINTK("exfat_rmdir exited\n"); ++ return err; ++} ++ ++static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ struct inode *old_inode, *new_inode; ++ struct super_block *sb = old_dir->i_sb; ++ struct timespec ts; ++ loff_t i_pos; ++ int err; ++ ++ __lock_super(sb); ++ ++ PRINTK("exfat_rename entered\n"); ++ ++ old_inode = old_dentry->d_inode; ++ new_inode = new_dentry->d_inode; ++ ++ ts = CURRENT_TIME_SEC; ++ ++ EXFAT_I(old_inode)->fid.size = i_size_read(old_inode); ++ ++ err = FsMoveFile(old_dir, &(EXFAT_I(old_inode)->fid), new_dir, new_dentry); ++ if (err) { ++ if (err == FFS_PERMISSIONERR) ++ err = -EPERM; ++ else if (err == FFS_INVALIDPATH) ++ err = -EINVAL; ++ else if (err == FFS_FILEEXIST) ++ err = -EEXIST; ++ else if (err == FFS_NOTFOUND) ++ err = -ENOENT; ++ else if (err == FFS_FULL) ++ err = -ENOSPC; ++ else ++ err = -EIO; ++ goto out; ++ } ++ new_dir->i_version++; ++ new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime = ts; ++ if (IS_DIRSYNC(new_dir)) ++ (void) exfat_sync_inode(new_dir); ++ else ++ mark_inode_dirty(new_dir); ++ ++ i_pos = ((loff_t) EXFAT_I(old_inode)->fid.dir.dir << 32) | ++ (EXFAT_I(old_inode)->fid.entry & 0xffffffff); ++ ++ exfat_detach(old_inode); ++ exfat_attach(old_inode, i_pos); ++ if (IS_DIRSYNC(new_dir)) ++ (void) exfat_sync_inode(old_inode); ++ else ++ mark_inode_dirty(old_inode); ++ ++ if ((S_ISDIR(old_inode->i_mode)) && (old_dir != new_dir)) { ++ drop_nlink(old_dir); ++ if (!new_inode) inc_nlink(new_dir); ++ } ++ ++ old_dir->i_version++; ++ old_dir->i_ctime = old_dir->i_mtime = ts; ++ if (IS_DIRSYNC(old_dir)) ++ (void) exfat_sync_inode(old_dir); ++ else ++ mark_inode_dirty(old_dir); ++ ++ if (new_inode) { ++ exfat_detach(new_inode); ++ drop_nlink(new_inode); ++ if (S_ISDIR(new_inode->i_mode)) ++ drop_nlink(new_inode); ++ new_inode->i_ctime = ts; ++ } ++ ++out: ++ __unlock_super(sb); ++ PRINTK("exfat_rename exited\n"); ++ return err; ++} ++ ++static int exfat_cont_expand(struct inode *inode, loff_t size) ++{ ++ struct address_space *mapping = inode->i_mapping; ++ loff_t start = i_size_read(inode), count = size - i_size_read(inode); ++ int err, err2; ++ ++ if ((err = generic_cont_expand_simple(inode, size)) != 0) ++ return err; ++ ++ inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; ++ mark_inode_dirty(inode); ++ ++ if (IS_SYNC(inode)) { ++ err = filemap_fdatawrite_range(mapping, start, start + count - 1); ++ err2 = sync_mapping_buffers(mapping); ++ err = (err)?(err):(err2); ++ err2 = write_inode_now(inode, 1); ++ err = (err)?(err):(err2); ++ if (!err) { ++ err = filemap_fdatawait_range(mapping, start, start + count - 1); ++ } ++ } ++ return err; ++} ++ ++static int exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode) ++{ ++ mode_t allow_utime = sbi->options.allow_utime; ++ ++ if (current_fsuid() != inode->i_uid) { ++ if (in_group_p(inode->i_gid)) ++ allow_utime >>= 3; ++ if (allow_utime & MAY_WRITE) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int exfat_sanitize_mode(const struct exfat_sb_info *sbi, ++ struct inode *inode, umode_t *mode_ptr) ++{ ++ mode_t i_mode, mask, perm; ++ ++ i_mode = inode->i_mode; ++ ++ if (S_ISREG(i_mode) || S_ISLNK(i_mode)) ++ mask = sbi->options.fs_fmask; ++ else ++ mask = sbi->options.fs_dmask; ++ ++ perm = *mode_ptr & ~(S_IFMT | mask); ++ ++ if ((perm & (S_IRUGO | S_IXUGO)) != (i_mode & (S_IRUGO|S_IXUGO))) ++ return -EPERM; ++ ++ if (exfat_mode_can_hold_ro(inode)) { ++ if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask))) ++ return -EPERM; ++ } else { ++ if ((perm & S_IWUGO) != (S_IWUGO & ~mask)) ++ return -EPERM; ++ } ++ ++ *mode_ptr &= S_IFMT | perm; ++ ++ return 0; ++} ++ ++static int exfat_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ ++ struct exfat_sb_info *sbi = EXFAT_SB(dentry->d_sb); ++ struct inode *inode = dentry->d_inode; ++ unsigned int ia_valid; ++ int error; ++ loff_t old_size; ++ ++ PRINTK("exfat_setattr entered\n"); ++ ++ if ((attr->ia_valid & ATTR_SIZE) ++ && (attr->ia_size > i_size_read(inode))) { ++ error = exfat_cont_expand(inode, attr->ia_size); ++ if (error || attr->ia_valid == ATTR_SIZE) ++ return error; ++ attr->ia_valid &= ~ATTR_SIZE; ++ } ++ ++ ia_valid = attr->ia_valid; ++ ++ if ((ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) ++ && exfat_allow_set_time(sbi, inode)) { ++ attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET); ++ } ++ ++ error = inode_change_ok(inode, attr); ++ attr->ia_valid = ia_valid; ++ if (error) { ++ return error; ++ } ++ ++ if (((attr->ia_valid & ATTR_UID) && ++ (attr->ia_uid != sbi->options.fs_uid)) || ++ ((attr->ia_valid & ATTR_GID) && ++ (attr->ia_gid != sbi->options.fs_gid)) || ++ ((attr->ia_valid & ATTR_MODE) && ++ (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | S_IRWXUGO)))) { ++ return -EPERM; ++ } ++ ++ if (attr->ia_valid & ATTR_MODE) { ++ if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0) ++ attr->ia_valid &= ~ATTR_MODE; ++ } ++ ++ EXFAT_I(inode)->fid.size = i_size_read(inode); ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ++ if (attr->ia_valid) ++ error = inode_setattr(inode, attr); ++#else ++ if (attr->ia_valid & ATTR_SIZE) { ++ old_size = i_size_read(inode); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) ++ down_write(&EXFAT_I(inode)->truncate_lock); ++ truncate_setsize(inode, attr->ia_size); ++ _exfat_truncate(inode, old_size); ++ up_write(&EXFAT_I(inode)->truncate_lock); ++#else ++ truncate_setsize(inode, attr->ia_size); ++ _exfat_truncate(inode, old_size); ++#endif ++ } ++ setattr_copy(inode, attr); ++ mark_inode_dirty(inode); ++#endif ++ ++ ++ PRINTK("exfat_setattr exited\n"); ++ return error; ++} ++ ++static int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) ++{ ++ struct inode *inode = dentry->d_inode; ++ ++ PRINTK("exfat_getattr entered\n"); ++ ++ generic_fillattr(inode, stat); ++ stat->blksize = EXFAT_SB(inode->i_sb)->fs_info.cluster_size; ++ ++ PRINTK("exfat_getattr exited\n"); ++ return 0; ++} ++ ++const struct inode_operations exfat_dir_inode_operations = { ++ .create = exfat_create, ++ .lookup = exfat_lookup, ++ .unlink = exfat_unlink, ++ .symlink = exfat_symlink, ++ .mkdir = exfat_mkdir, ++ .rmdir = exfat_rmdir, ++ .rename = exfat_rename, ++ .setattr = exfat_setattr, ++ .getattr = exfat_getattr, ++}; ++ ++static void *exfat_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ struct exfat_inode_info *ei = EXFAT_I(dentry->d_inode); ++ nd_set_link(nd, (char *)(ei->target)); ++ return NULL; ++} ++ ++const struct inode_operations exfat_symlink_inode_operations = { ++ .readlink = generic_readlink, ++ .follow_link = exfat_follow_link, ++}; ++ ++static int exfat_file_release(struct inode *inode, struct file *filp) ++{ ++ struct super_block *sb = inode->i_sb; ++ ++ EXFAT_I(inode)->fid.size = i_size_read(inode); ++ FsSyncVol(sb, 0); ++ return 0; ++} ++ ++const struct file_operations exfat_file_operations = { ++ .llseek = generic_file_llseek, ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = generic_file_aio_write, ++ .mmap = generic_file_mmap, ++ .release = exfat_file_release, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ++ .ioctl = exfat_generic_ioctl, ++ .fsync = exfat_file_fsync, ++#else ++ .unlocked_ioctl = exfat_generic_ioctl, ++ .fsync = generic_file_fsync, ++#endif ++ .splice_read = generic_file_splice_read, ++}; ++ ++static void _exfat_truncate(struct inode *inode, loff_t old_size) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ FS_INFO_T *p_fs = &(sbi->fs_info); ++ int err; ++ ++ __lock_super(sb); ++ ++ if (EXFAT_I(inode)->mmu_private > i_size_read(inode)) ++ EXFAT_I(inode)->mmu_private = i_size_read(inode); ++ ++ if (EXFAT_I(inode)->fid.start_clu == 0) goto out; ++ ++ err = FsTruncateFile(inode, old_size, i_size_read(inode)); ++ if (err) goto out; ++ ++ inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; ++ if (IS_DIRSYNC(inode)) ++ (void) exfat_sync_inode(inode); ++ else ++ mark_inode_dirty(inode); ++ ++ inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) ++ & ~((loff_t)p_fs->cluster_size - 1)) >> 9; ++out: ++ __unlock_super(sb); ++} ++ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36) ++static void exfat_truncate(struct inode *inode) ++{ ++ _exfat_truncate(inode, i_size_read(inode)); ++} ++#endif ++ ++const struct inode_operations exfat_file_inode_operations = { ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36) ++ .truncate = exfat_truncate, ++#endif ++ .setattr = exfat_setattr, ++ .getattr = exfat_getattr, ++}; ++ ++static int exfat_bmap(struct inode *inode, sector_t sector, sector_t *phys, ++ unsigned long *mapped_blocks, int *create) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ FS_INFO_T *p_fs = &(sbi->fs_info); ++ BD_INFO_T *p_bd = &(sbi->bd_info); ++ const unsigned long blocksize = sb->s_blocksize; ++ const unsigned char blocksize_bits = sb->s_blocksize_bits; ++ sector_t last_block; ++ int err, clu_offset, sec_offset; ++ unsigned int cluster; ++ ++ *phys = 0; ++ *mapped_blocks = 0; ++ ++ if ((p_fs->vol_type == FAT12) || (p_fs->vol_type == FAT16)) { ++ if (inode->i_ino == EXFAT_ROOT_INO) { ++ if (sector < (p_fs->dentries_in_root >> (p_bd->sector_size_bits-DENTRY_SIZE_BITS))) { ++ *phys = sector + p_fs->root_start_sector; ++ *mapped_blocks = 1; ++ } ++ return 0; ++ } ++ } ++ ++ last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits; ++ if (sector >= last_block) { ++ if (*create == 0) return 0; ++ } else { ++ *create = 0; ++ } ++ ++ clu_offset = sector >> p_fs->sectors_per_clu_bits; ++ sec_offset = sector & (p_fs->sectors_per_clu - 1); ++ ++ EXFAT_I(inode)->fid.size = i_size_read(inode); ++ ++ err = FsMapCluster(inode, clu_offset, &cluster); ++ ++ if (err) { ++ if (err == FFS_FULL) ++ return -ENOSPC; ++ else ++ return -EIO; ++ } else if (cluster != CLUSTER_32(~0)) { ++ *phys = START_SECTOR(cluster) + sec_offset; ++ *mapped_blocks = p_fs->sectors_per_clu - sec_offset; ++ } ++ ++ return 0; ++} ++ ++static int exfat_get_block(struct inode *inode, sector_t iblock, ++ struct buffer_head *bh_result, int create) ++{ ++ struct super_block *sb = inode->i_sb; ++ unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; ++ int err; ++ unsigned long mapped_blocks; ++ sector_t phys; ++ ++ __lock_super(sb); ++ ++ err = exfat_bmap(inode, iblock, &phys, &mapped_blocks, &create); ++ if (err) { ++ __unlock_super(sb); ++ return err; ++ } ++ ++ if (phys) { ++ max_blocks = min(mapped_blocks, max_blocks); ++ if (create) { ++ EXFAT_I(inode)->mmu_private += max_blocks << sb->s_blocksize_bits; ++ set_buffer_new(bh_result); ++ } ++ map_bh(bh_result, sb, phys); ++ } ++ ++ bh_result->b_size = max_blocks << sb->s_blocksize_bits; ++ __unlock_super(sb); ++ ++ return 0; ++} ++ ++static int exfat_readpage(struct file *file, struct page *page) ++{ ++ int ret; ++ ret = mpage_readpage(page, exfat_get_block); ++ return ret; ++} ++ ++static int exfat_readpages(struct file *file, struct address_space *mapping, ++ struct list_head *pages, unsigned nr_pages) ++{ ++ int ret; ++ ret = mpage_readpages(mapping, pages, nr_pages, exfat_get_block); ++ return ret; ++} ++ ++static int exfat_writepage(struct page *page, struct writeback_control *wbc) ++{ ++ int ret; ++ ret = block_write_full_page(page, exfat_get_block, wbc); ++ return ret; ++} ++ ++static int exfat_writepages(struct address_space *mapping, ++ struct writeback_control *wbc) ++{ ++ int ret; ++ ret = mpage_writepages(mapping, wbc, exfat_get_block); ++ return ret; ++} ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) ++static void exfat_write_failed(struct address_space *mapping, loff_t to) ++{ ++ struct inode *inode = mapping->host; ++ if (to > i_size_read(inode)) { ++ truncate_pagecache(inode, to, i_size_read(inode)); ++ EXFAT_I(inode)->fid.size = i_size_read(inode); ++ _exfat_truncate(inode, i_size_read(inode)); ++ } ++} ++#endif ++ ++ ++static int exfat_write_begin(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ ++ int ret; ++ *pagep = NULL; ++ ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, ++ exfat_get_block, ++ &EXFAT_I(mapping->host)->mmu_private); ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) ++ if (ret < 0) ++ exfat_write_failed(mapping, pos+len); ++#endif ++ return ret; ++} ++ ++static int exfat_write_end(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *pagep, void *fsdata) ++{ ++ struct inode *inode = mapping->host; ++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); ++ int err; ++ ++ err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata); ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) ++ if (err < len) ++ exfat_write_failed(mapping, pos+len); ++#endif ++ ++ if (!(err < 0) && !(fid->attr & ATTR_ARCHIVE)) { ++ inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; ++ fid->attr |= ATTR_ARCHIVE; ++ mark_inode_dirty(inode); ++ } ++ return err; ++} ++ ++static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, ++ const struct iovec *iov, ++ loff_t offset, unsigned long nr_segs) ++{ ++ struct inode *inode = iocb->ki_filp->f_mapping->host; ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) ++ struct address_space *mapping = iocb->ki_filp->f_mapping; ++#endif ++ ssize_t ret; ++ ++ if (rw == WRITE) { ++ if (EXFAT_I(inode)->mmu_private < (offset + iov_length(iov, nr_segs))) ++ return 0; ++ } ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) ++ ret = blockdev_direct_IO(rw, iocb, inode, iov, ++ offset, nr_segs, exfat_get_block); ++#else ++ ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, ++ offset, nr_segs, exfat_get_block, NULL); ++#endif ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) ++ if ((ret < 0) && (rw & WRITE)) ++ exfat_write_failed(mapping, offset+iov_length(iov, nr_segs)); ++#endif ++ return ret; ++ ++} ++ ++static sector_t _exfat_bmap(struct address_space *mapping, sector_t block) ++{ ++ sector_t blocknr; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) ++ down_read(&EXFAT_I(mapping->host)->truncate_lock); ++ blocknr = generic_block_bmap(mapping, block, exfat_get_block); ++ up_read(&EXFAT_I(mapping->host)->truncate_lock); ++#else ++ down_read(&mapping->host->i_alloc_sem); ++ blocknr = generic_block_bmap(mapping, block, exfat_get_block); ++ up_read(&mapping->host->i_alloc_sem); ++#endif ++ ++ return blocknr; ++} ++ ++const struct address_space_operations exfat_aops = { ++ .readpage = exfat_readpage, ++ .readpages = exfat_readpages, ++ .writepage = exfat_writepage, ++ .writepages = exfat_writepages, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) ++ .sync_page = block_sync_page, ++#endif ++ .write_begin = exfat_write_begin, ++ .write_end = exfat_write_end, ++ .direct_IO = exfat_direct_IO, ++ .bmap = _exfat_bmap ++}; ++ ++static inline unsigned long exfat_hash(loff_t i_pos) ++{ ++ return hash_32(i_pos, EXFAT_HASH_BITS); ++} ++ ++static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos) { ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ struct exfat_inode_info *info; ++ struct hlist_node *node; ++ struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); ++ struct inode *inode = NULL; ++ ++ spin_lock(&sbi->inode_hash_lock); ++ hlist_for_each_entry(info, node, head, i_hash_fat) { ++ CHECK_ERR(info->vfs_inode.i_sb != sb); ++ ++ if (i_pos != info->i_pos) ++ continue; ++ inode = igrab(&info->vfs_inode); ++ if (inode) ++ break; ++ } ++ spin_unlock(&sbi->inode_hash_lock); ++ return inode; ++} ++ ++static void exfat_attach(struct inode *inode, loff_t i_pos) ++{ ++ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); ++ struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); ++ ++ spin_lock(&sbi->inode_hash_lock); ++ EXFAT_I(inode)->i_pos = i_pos; ++ hlist_add_head(&EXFAT_I(inode)->i_hash_fat, head); ++ spin_unlock(&sbi->inode_hash_lock); ++} ++ ++static void exfat_detach(struct inode *inode) ++{ ++ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); ++ ++ spin_lock(&sbi->inode_hash_lock); ++ hlist_del_init(&EXFAT_I(inode)->i_hash_fat); ++ EXFAT_I(inode)->i_pos = 0; ++ spin_unlock(&sbi->inode_hash_lock); ++} ++ ++static int exfat_fill_inode(struct inode *inode, FILE_ID_T *fid) ++{ ++ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); ++ FS_INFO_T *p_fs = &(sbi->fs_info); ++ DIR_ENTRY_T info; ++ ++ memcpy(&(EXFAT_I(inode)->fid), fid, sizeof(FILE_ID_T)); ++ ++ FsReadStat(inode, &info); ++ ++ EXFAT_I(inode)->i_pos = 0; ++ EXFAT_I(inode)->target = NULL; ++ inode->i_uid = sbi->options.fs_uid; ++ inode->i_gid = sbi->options.fs_gid; ++ inode->i_version++; ++ inode->i_generation = get_seconds(); ++ ++ if (info.Attr & ATTR_SUBDIR) { ++ inode->i_generation &= ~1; ++ inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); ++ inode->i_op = &exfat_dir_inode_operations; ++ inode->i_fop = &exfat_dir_operations; ++ ++ i_size_write(inode, info.Size); ++ EXFAT_I(inode)->mmu_private = i_size_read(inode); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) ++ set_nlink(inode,info.NumSubdirs); ++#else ++ inode->i_nlink = info.NumSubdirs; ++#endif ++ } else if (info.Attr & ATTR_SYMLINK) { ++ inode->i_generation |= 1; ++ inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); ++ inode->i_op = &exfat_symlink_inode_operations; ++ ++ i_size_write(inode, info.Size); ++ EXFAT_I(inode)->mmu_private = i_size_read(inode); ++ } else { ++ inode->i_generation |= 1; ++ inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); ++ inode->i_op = &exfat_file_inode_operations; ++ inode->i_fop = &exfat_file_operations; ++ inode->i_mapping->a_ops = &exfat_aops; ++ inode->i_mapping->nrpages = 0; ++ ++ i_size_write(inode, info.Size); ++ EXFAT_I(inode)->mmu_private = i_size_read(inode); ++ } ++ exfat_save_attr(inode, info.Attr); ++ ++ inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) ++ & ~((loff_t)p_fs->cluster_size - 1)) >> 9; ++ ++ exfat_time_fat2unix(sbi, &inode->i_mtime, &info.ModifyTimestamp); ++ exfat_time_fat2unix(sbi, &inode->i_ctime, &info.CreateTimestamp); ++ exfat_time_fat2unix(sbi, &inode->i_atime, &info.AccessTimestamp); ++ ++ return 0; ++} ++ ++static struct inode *exfat_build_inode(struct super_block *sb, ++ FILE_ID_T *fid, loff_t i_pos) { ++ struct inode *inode; ++ int err; ++ ++ inode = exfat_iget(sb, i_pos); ++ if (inode) ++ goto out; ++ inode = new_inode(sb); ++ if (!inode) { ++ inode = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ inode->i_ino = iunique(sb, EXFAT_ROOT_INO); ++ inode->i_version = 1; ++ err = exfat_fill_inode(inode, fid); ++ if (err) { ++ iput(inode); ++ inode = ERR_PTR(err); ++ goto out; ++ } ++ exfat_attach(inode, i_pos); ++ insert_inode_hash(inode); ++out: ++ return inode; ++} ++ ++static int exfat_sync_inode(struct inode *inode) ++{ ++ return exfat_write_inode(inode, NULL); ++} ++ ++static struct inode *exfat_alloc_inode(struct super_block *sb) { ++ struct exfat_inode_info *ei; ++ ++ ei = kmem_cache_alloc(exfat_inode_cachep, GFP_NOFS); ++ if (!ei) ++ return NULL; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) ++ init_rwsem(&ei->truncate_lock); ++#endif ++ ++ return &ei->vfs_inode; ++} ++ ++static void exfat_destroy_inode(struct inode *inode) ++{ ++ FREE(EXFAT_I(inode)->target); ++ EXFAT_I(inode)->target = NULL; ++ ++ kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode)); ++} ++ ++static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ DIR_ENTRY_T info; ++ ++ if (inode->i_ino == EXFAT_ROOT_INO) ++ return 0; ++ ++ info.Attr = exfat_make_attr(inode); ++ info.Size = i_size_read(inode); ++ ++ exfat_time_unix2fat(sbi, &inode->i_mtime, &info.ModifyTimestamp); ++ exfat_time_unix2fat(sbi, &inode->i_ctime, &info.CreateTimestamp); ++ exfat_time_unix2fat(sbi, &inode->i_atime, &info.AccessTimestamp); ++ ++ FsWriteStat(inode, &info); ++ ++ return 0; ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ++static void exfat_delete_inode(struct inode *inode) ++{ ++ truncate_inode_pages(&inode->i_data, 0); ++ clear_inode(inode); ++} ++ ++static void exfat_clear_inode(struct inode *inode) ++{ ++ exfat_detach(inode); ++ remove_inode_hash(inode); ++} ++#else ++static void exfat_evict_inode(struct inode *inode) ++{ ++ truncate_inode_pages(&inode->i_data, 0); ++ ++ if (!inode->i_nlink) ++ i_size_write(inode, 0); ++ invalidate_inode_buffers(inode); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,00) ++ end_writeback(inode); ++#else ++ clear_inode(inode); ++#endif ++ exfat_detach(inode); ++ ++ remove_inode_hash(inode); ++} ++#endif ++ ++ ++static void exfat_put_super(struct super_block *sb) ++{ ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ if (__is_sb_dirty(sb)) ++ exfat_write_super(sb); ++ ++ FsUmountVol(sb); ++ ++ if (sbi->nls_disk) { ++ unload_nls(sbi->nls_disk); ++ sbi->nls_disk = NULL; ++ sbi->options.codepage = exfat_default_codepage; ++ } ++ if (sbi->nls_io) { ++ unload_nls(sbi->nls_io); ++ sbi->nls_io = NULL; ++ } ++ if (sbi->options.iocharset != exfat_default_iocharset) { ++ kfree(sbi->options.iocharset); ++ sbi->options.iocharset = exfat_default_iocharset; ++ } ++ ++ sb->s_fs_info = NULL; ++ kfree(sbi); ++} ++ ++static void exfat_write_super(struct super_block *sb) ++{ ++ __lock_super(sb); ++ ++ __set_sb_clean(sb); ++ ++ if (!(sb->s_flags & MS_RDONLY)) ++ FsSyncVol(sb, 1); ++ ++ __unlock_super(sb); ++} ++ ++static int exfat_sync_fs(struct super_block *sb, int wait) ++{ ++ int err = 0; ++ ++ if (__is_sb_dirty(sb)) { ++ __lock_super(sb); ++ __set_sb_clean(sb); ++ err = FsSyncVol(sb, 1); ++ __unlock_super(sb); ++ } ++ ++ return err; ++} ++ ++static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct super_block *sb = dentry->d_sb; ++ u64 id = huge_encode_dev(sb->s_bdev->bd_dev); ++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); ++ VOL_INFO_T info; ++ ++ if (p_fs->used_clusters == (UINT32) ~0) { ++ if (FFS_MEDIAERR == FsGetVolInfo(sb, &info)) ++ return -EIO; ++ ++ } else { ++ info.FatType = p_fs->vol_type; ++ info.ClusterSize = p_fs->cluster_size; ++ info.NumClusters = p_fs->num_clusters - 2; ++ info.UsedClusters = p_fs->used_clusters; ++ info.FreeClusters = info.NumClusters - info.UsedClusters; ++ ++ if (p_fs->dev_ejected) ++ return -EIO; ++ } ++ ++ buf->f_type = sb->s_magic; ++ buf->f_bsize = info.ClusterSize; ++ buf->f_blocks = info.NumClusters; ++ buf->f_bfree = info.FreeClusters; ++ buf->f_bavail = info.FreeClusters; ++ buf->f_fsid.val[0] = (u32)id; ++ buf->f_fsid.val[1] = (u32)(id >> 32); ++ buf->f_namelen = 260; ++ ++ return 0; ++} ++ ++static int exfat_remount(struct super_block *sb, int *flags, char *data) ++{ ++ *flags |= MS_NODIRATIME; ++ return 0; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) ++static int exfat_show_options(struct seq_file *m, struct dentry *root) ++{ ++ struct exfat_sb_info *sbi = EXFAT_SB(root->d_sb); ++#else ++static int exfat_show_options(struct seq_file *m, struct vfsmount *mnt) ++{ ++ struct exfat_sb_info *sbi = EXFAT_SB(mnt->mnt_sb); ++#endif ++ struct exfat_mount_options *opts = &sbi->options; ++ ++ if (opts->fs_uid != 0) ++ seq_printf(m, ",uid=%u", opts->fs_uid); ++ if (opts->fs_gid != 0) ++ seq_printf(m, ",gid=%u", opts->fs_gid); ++ seq_printf(m, ",fmask=%04o", opts->fs_fmask); ++ seq_printf(m, ",dmask=%04o", opts->fs_dmask); ++ if (opts->allow_utime) ++ seq_printf(m, ",allow_utime=%04o", opts->allow_utime); ++ if (sbi->nls_disk) ++ seq_printf(m, ",codepage=%s", sbi->nls_disk->charset); ++ if (sbi->nls_io) ++ seq_printf(m, ",iocharset=%s", sbi->nls_io->charset); ++ seq_printf(m, ",namecase=%u", opts->casesensitive); ++ if (opts->errors == EXFAT_ERRORS_CONT) ++ seq_puts(m, ",errors=continue"); ++ else if (opts->errors == EXFAT_ERRORS_PANIC) ++ seq_puts(m, ",errors=panic"); ++ else ++ seq_puts(m, ",errors=remount-ro"); ++#if EXFAT_CONFIG_DISCARD ++ if (opts->discard) ++ seq_printf(m, ",discard"); ++#endif ++ return 0; ++} ++ ++const struct super_operations exfat_sops = { ++ .alloc_inode = exfat_alloc_inode, ++ .destroy_inode = exfat_destroy_inode, ++ .write_inode = exfat_write_inode, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ++ .delete_inode = exfat_delete_inode, ++ .clear_inode = exfat_clear_inode, ++#else ++ .evict_inode = exfat_evict_inode, ++#endif ++ .put_super = exfat_put_super, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,00) ++ .write_super = exfat_write_super, ++#endif ++ .sync_fs = exfat_sync_fs, ++ .statfs = exfat_statfs, ++ .remount_fs = exfat_remount, ++ .show_options = exfat_show_options, ++}; ++ ++enum { ++ Opt_uid, ++ Opt_gid, ++ Opt_umask, ++ Opt_dmask, ++ Opt_fmask, ++ Opt_allow_utime, ++ Opt_codepage, ++ Opt_charset, ++ Opt_namecase, ++ Opt_debug, ++ Opt_err_cont, ++ Opt_err_panic, ++ Opt_err_ro, ++ Opt_err, ++#if EXFAT_CONFIG_DISCARD ++ Opt_discard, ++#endif ++}; ++ ++static const match_table_t exfat_tokens = { ++ {Opt_uid, "uid=%u"}, ++ {Opt_gid, "gid=%u"}, ++ {Opt_umask, "umask=%o"}, ++ {Opt_dmask, "dmask=%o"}, ++ {Opt_fmask, "fmask=%o"}, ++ {Opt_allow_utime, "allow_utime=%o"}, ++ {Opt_codepage, "codepage=%u"}, ++ {Opt_charset, "iocharset=%s"}, ++ {Opt_namecase, "namecase=%u"}, ++ {Opt_debug, "debug"}, ++ {Opt_err_cont, "errors=continue"}, ++ {Opt_err_panic, "errors=panic"}, ++ {Opt_err_ro, "errors=remount-ro"}, ++#if EXFAT_CONFIG_DISCARD ++ {Opt_discard, "discard"}, ++#endif ++ {Opt_err, NULL} ++}; ++ ++static int parse_options(char *options, int silent, int *debug, ++ struct exfat_mount_options *opts) ++{ ++ char *p; ++ substring_t args[MAX_OPT_ARGS]; ++ int option; ++ char *iocharset; ++ ++ opts->fs_uid = current_uid(); ++ opts->fs_gid = current_gid(); ++ opts->fs_fmask = opts->fs_dmask = current->fs->umask; ++ opts->allow_utime = (unsigned short) -1; ++ opts->codepage = exfat_default_codepage; ++ opts->iocharset = exfat_default_iocharset; ++ opts->casesensitive = 0; ++ opts->errors = EXFAT_ERRORS_RO; ++#if EXFAT_CONFIG_DISCARD ++ opts->discard = 0; ++#endif ++ *debug = 0; ++ ++ if (!options) ++ goto out; ++ ++ while ((p = strsep(&options, ",")) != NULL) { ++ int token; ++ if (!*p) ++ continue; ++ ++ token = match_token(p, exfat_tokens, args); ++ switch (token) { ++ case Opt_uid: ++ if (match_int(&args[0], &option)) ++ return 0; ++ opts->fs_uid = option; ++ break; ++ case Opt_gid: ++ if (match_int(&args[0], &option)) ++ return 0; ++ opts->fs_gid = option; ++ break; ++ case Opt_umask: ++ case Opt_dmask: ++ case Opt_fmask: ++ if (match_octal(&args[0], &option)) ++ return 0; ++ if (token != Opt_dmask) ++ opts->fs_fmask = option; ++ if (token != Opt_fmask) ++ opts->fs_dmask = option; ++ break; ++ case Opt_allow_utime: ++ if (match_octal(&args[0], &option)) ++ return 0; ++ opts->allow_utime = option & (S_IWGRP | S_IWOTH); ++ break; ++ case Opt_codepage: ++ if (match_int(&args[0], &option)) ++ return 0; ++ opts->codepage = option; ++ break; ++ case Opt_charset: ++ if (opts->iocharset != exfat_default_iocharset) ++ kfree(opts->iocharset); ++ iocharset = match_strdup(&args[0]); ++ if (!iocharset) ++ return -ENOMEM; ++ opts->iocharset = iocharset; ++ break; ++ case Opt_namecase: ++ if (match_int(&args[0], &option)) ++ return 0; ++ opts->casesensitive = option; ++ break; ++ case Opt_err_cont: ++ opts->errors = EXFAT_ERRORS_CONT; ++ break; ++ case Opt_err_panic: ++ opts->errors = EXFAT_ERRORS_PANIC; ++ break; ++ case Opt_err_ro: ++ opts->errors = EXFAT_ERRORS_RO; ++ break; ++ case Opt_debug: ++ *debug = 1; ++ break; ++#if EXFAT_CONFIG_DISCARD ++ case Opt_discard: ++ opts->discard = 1; ++ break; ++#endif ++ default: ++ if (!silent) { ++ printk(KERN_ERR "[EXFAT] Unrecognized mount option %s or missing value\n", p); ++ } ++ return -EINVAL; ++ } ++ } ++ ++out: ++ if (opts->allow_utime == (unsigned short) -1) ++ opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH); ++ ++ return 0; ++} ++ ++static void exfat_hash_init(struct super_block *sb) ++{ ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ int i; ++ ++ spin_lock_init(&sbi->inode_hash_lock); ++ for (i = 0; i < EXFAT_HASH_SIZE; i++) ++ INIT_HLIST_HEAD(&sbi->inode_hashtable[i]); ++} ++ ++static int exfat_read_root(struct inode *inode) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ struct timespec ts; ++ FS_INFO_T *p_fs = &(sbi->fs_info); ++ DIR_ENTRY_T info; ++ ++ ts = CURRENT_TIME_SEC; ++ ++ EXFAT_I(inode)->fid.dir.dir = p_fs->root_dir; ++ EXFAT_I(inode)->fid.dir.flags = 0x01; ++ EXFAT_I(inode)->fid.entry = -1; ++ EXFAT_I(inode)->fid.start_clu = p_fs->root_dir; ++ EXFAT_I(inode)->fid.flags = 0x01; ++ EXFAT_I(inode)->fid.type = TYPE_DIR; ++ EXFAT_I(inode)->fid.rwoffset = 0; ++ EXFAT_I(inode)->fid.hint_last_off = -1; ++ ++ EXFAT_I(inode)->target = NULL; ++ ++ FsReadStat(inode, &info); ++ ++ inode->i_uid = sbi->options.fs_uid; ++ inode->i_gid = sbi->options.fs_gid; ++ inode->i_version++; ++ inode->i_generation = 0; ++ inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, S_IRWXUGO); ++ inode->i_op = &exfat_dir_inode_operations; ++ inode->i_fop = &exfat_dir_operations; ++ ++ i_size_write(inode, info.Size); ++ inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) ++ & ~((loff_t)p_fs->cluster_size - 1)) >> 9; ++ EXFAT_I(inode)->i_pos = ((loff_t) p_fs->root_dir << 32) | 0xffffffff; ++ EXFAT_I(inode)->mmu_private = i_size_read(inode); ++ ++ exfat_save_attr(inode, ATTR_SUBDIR); ++ inode->i_mtime = inode->i_atime = inode->i_ctime = ts; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) ++ set_nlink(inode,info.NumSubdirs + 2); ++#else ++ inode->i_nlink = info.NumSubdirs + 2; ++#endif ++ ++ return 0; ++} ++ ++static int exfat_fill_super(struct super_block *sb, void *data, int silent) ++{ ++ struct inode *root_inode = NULL; ++ struct exfat_sb_info *sbi; ++ int debug, ret; ++ long error; ++ char buf[50]; ++ ++ sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL); ++ if (!sbi) ++ return -ENOMEM; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) ++ mutex_init(&sbi->s_lock); ++#endif ++ sb->s_fs_info = sbi; ++ ++ sb->s_flags |= MS_NODIRATIME; ++ sb->s_magic = EXFAT_SUPER_MAGIC; ++ sb->s_op = &exfat_sops; ++ ++ error = parse_options(data, silent, &debug, &sbi->options); ++ if (error) ++ goto out_fail; ++ ++ error = -EIO; ++ sb_min_blocksize(sb, 512); ++ sb->s_maxbytes = 0x7fffffffffffffffLL; ++ ++ ret = FsMountVol(sb); ++ if (ret) { ++ if (!silent) ++ printk(KERN_ERR "[EXFAT] FsMountVol failed\n"); ++ ++ goto out_fail; ++ } ++ ++ exfat_hash_init(sb); ++ ++ error = -EINVAL; ++ sprintf(buf, "cp%d", sbi->options.codepage); ++ sbi->nls_disk = load_nls(buf); ++ if (!sbi->nls_disk) { ++ printk(KERN_ERR "[EXFAT] Codepage %s not found\n", buf); ++ goto out_fail2; ++ } ++ ++ sbi->nls_io = load_nls(sbi->options.iocharset); ++ if (!sbi->nls_io) { ++ printk(KERN_ERR "[EXFAT] IO charset %s not found\n", ++ sbi->options.iocharset); ++ goto out_fail2; ++ } ++ ++ error = -ENOMEM; ++ root_inode = new_inode(sb); ++ if (!root_inode) ++ goto out_fail2; ++ root_inode->i_ino = EXFAT_ROOT_INO; ++ root_inode->i_version = 1; ++ error = exfat_read_root(root_inode); ++ if (error < 0) ++ goto out_fail2; ++ error = -ENOMEM; ++ exfat_attach(root_inode, EXFAT_I(root_inode)->i_pos); ++ insert_inode_hash(root_inode); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) ++ sb->s_root = d_make_root(root_inode); ++#else ++ sb->s_root = d_alloc_root(root_inode); ++#endif ++ if (!sb->s_root) { ++ printk(KERN_ERR "[EXFAT] Getting the root inode failed\n"); ++ goto out_fail2; ++ } ++ ++ return 0; ++ ++out_fail2: ++ FsUmountVol(sb); ++out_fail: ++ if (root_inode) ++ iput(root_inode); ++ if (sbi->nls_io) ++ unload_nls(sbi->nls_io); ++ if (sbi->nls_disk) ++ unload_nls(sbi->nls_disk); ++ if (sbi->options.iocharset != exfat_default_iocharset) ++ kfree(sbi->options.iocharset); ++ sb->s_fs_info = NULL; ++ kfree(sbi); ++ return error; ++} ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++static int exfat_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *data, struct vfsmount *mnt) ++{ ++ return get_sb_bdev(fs_type, flags, dev_name, data, exfat_fill_super, mnt); ++} ++#else ++static struct dentry *exfat_fs_mount(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *data) { ++ return mount_bdev(fs_type, flags, dev_name, data, exfat_fill_super); ++} ++#endif ++ ++static void init_once(void *foo) ++{ ++ struct exfat_inode_info *ei = (struct exfat_inode_info *)foo; ++ ++ INIT_HLIST_NODE(&ei->i_hash_fat); ++ inode_init_once(&ei->vfs_inode); ++} ++ ++static int __init exfat_init_inodecache(void) ++{ ++ exfat_inode_cachep = kmem_cache_create("exfat_inode_cache", ++ sizeof(struct exfat_inode_info), ++ 0, (SLAB_RECLAIM_ACCOUNT| ++ SLAB_MEM_SPREAD), ++ init_once); ++ if (exfat_inode_cachep == NULL) ++ return -ENOMEM; ++ return 0; ++} ++ ++static void __exit exfat_destroy_inodecache(void) ++{ ++ kmem_cache_destroy(exfat_inode_cachep); ++} ++ ++#if EXFAT_CONFIG_KERNEL_DEBUG ++static void exfat_debug_kill_sb(struct super_block *sb) ++{ ++ struct exfat_sb_info *sbi = EXFAT_SB(sb); ++ struct block_device *bdev = sb->s_bdev; ++ ++ long flags; ++ ++ if (sbi) { ++ flags = sbi->debug_flags; ++ ++ if (flags & EXFAT_DEBUGFLAGS_INVALID_UMOUNT) { ++ FsReleaseCache(sb); ++ invalidate_bdev(bdev); ++ } ++ } ++ ++ kill_block_super(sb); ++} ++#endif ++ ++static struct file_system_type exfat_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "exfat", ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++ .get_sb = exfat_get_sb, ++#else ++ .mount = exfat_fs_mount, ++#endif ++#if EXFAT_CONFIG_KERNEL_DEBUG ++ .kill_sb = exfat_debug_kill_sb, ++#else ++ .kill_sb = kill_block_super, ++#endif ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++ ++static int __init init_exfat_fs(void) ++{ ++ int err; ++ ++ printk(KERN_INFO "exFAT: FS Version %s\n", EXFAT_VERSION); ++ ++ err = exfat_init_inodecache(); ++ if (err) return err; ++ ++ return register_filesystem(&exfat_fs_type); ++} ++ ++static void __exit exit_exfat_fs(void) ++{ ++ exfat_destroy_inodecache(); ++ unregister_filesystem(&exfat_fs_type); ++} ++ ++module_init(init_exfat_fs); ++module_exit(exit_exfat_fs); ++ ++MODULE_LICENSE("GPL"); +diff --git a/drivers/staging/exfat/exfat_super.h b/drivers/staging/exfat/exfat_super.h +new file mode 100644 +index 00000000..22f4ee26 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_super.h +@@ -0,0 +1,149 @@ ++/* Some of the source code in this file came from "linux/fs/fat/fat.h". */ ++ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _EXFAT_LINUX_H ++#define _EXFAT_LINUX_H ++ ++#include <linux/buffer_head.h> ++#include <linux/string.h> ++#include <linux/nls.h> ++#include <linux/fs.h> ++#include <linux/mutex.h> ++#include <linux/swap.h> ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++#include "exfat_data.h" ++#include "exfat_oal.h" ++ ++#include "exfat_blkdev.h" ++#include "exfat_cache.h" ++#include "exfat_part.h" ++#include "exfat_nls.h" ++#include "exfat_api.h" ++#include "exfat.h" ++ ++#define EXFAT_ERRORS_CONT 1 ++#define EXFAT_ERRORS_PANIC 2 ++#define EXFAT_ERRORS_RO 3 ++ ++#define EXFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32) ++ ++struct exfat_mount_options { ++ uid_t fs_uid; ++ gid_t fs_gid; ++ unsigned short fs_fmask; ++ unsigned short fs_dmask; ++ unsigned short allow_utime; ++ unsigned short codepage; ++ char *iocharset; ++ unsigned char casesensitive; ++ unsigned char errors; ++#if EXFAT_CONFIG_DISCARD ++ unsigned char discard; ++#endif ++}; ++ ++#define EXFAT_HASH_BITS 8 ++#define EXFAT_HASH_SIZE (1UL << EXFAT_HASH_BITS) ++ ++struct exfat_sb_info { ++ FS_INFO_T fs_info; ++ BD_INFO_T bd_info; ++ ++ struct exfat_mount_options options; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) ++ int s_dirt; ++ struct mutex s_lock; ++#endif ++ struct nls_table *nls_disk; ++ struct nls_table *nls_io; ++ ++ struct inode *fat_inode; ++ ++ spinlock_t inode_hash_lock; ++ struct hlist_head inode_hashtable[EXFAT_HASH_SIZE]; ++#if EXFAT_CONFIG_KERNEL_DEBUG ++ long debug_flags; ++#endif ++}; ++ ++struct exfat_inode_info { ++ FILE_ID_T fid; ++ char *target; ++ loff_t mmu_private; ++ loff_t i_pos; ++ struct hlist_node i_hash_fat; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) ++ struct rw_semaphore truncate_lock; ++#endif ++ struct inode vfs_inode; ++}; ++ ++#define EXFAT_SB(sb) ((struct exfat_sb_info *)((sb)->s_fs_info)) ++ ++static inline struct exfat_inode_info *EXFAT_I(struct inode *inode) { ++ return container_of(inode, struct exfat_inode_info, vfs_inode); ++} ++ ++static inline int exfat_mode_can_hold_ro(struct inode *inode) ++{ ++ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); ++ ++ if (S_ISDIR(inode->i_mode)) ++ return 0; ++ ++ if ((~sbi->options.fs_fmask) & S_IWUGO) ++ return 1; ++ return 0; ++} ++ ++static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi, ++ u32 attr, mode_t mode) ++{ ++ if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR)) ++ mode &= ~S_IWUGO; ++ ++ if (attr & ATTR_SUBDIR) ++ return (mode & ~sbi->options.fs_dmask) | S_IFDIR; ++ else if (attr & ATTR_SYMLINK) ++ return (mode & ~sbi->options.fs_dmask) | S_IFLNK; ++ else ++ return (mode & ~sbi->options.fs_fmask) | S_IFREG; ++} ++ ++static inline u32 exfat_make_attr(struct inode *inode) ++{ ++ if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO)) ++ return ((EXFAT_I(inode)->fid.attr) | ATTR_READONLY); ++ else ++ return (EXFAT_I(inode)->fid.attr); ++} ++ ++static inline void exfat_save_attr(struct inode *inode, u32 attr) ++{ ++ if (exfat_mode_can_hold_ro(inode)) ++ EXFAT_I(inode)->fid.attr = attr & ATTR_RWMASK; ++ else ++ EXFAT_I(inode)->fid.attr = attr & (ATTR_RWMASK | ATTR_READONLY); ++} ++ ++#endif +diff --git a/drivers/staging/exfat/exfat_upcase.c b/drivers/staging/exfat/exfat_upcase.c +new file mode 100644 +index 00000000..7378a157 +--- /dev/null ++++ b/drivers/staging/exfat/exfat_upcase.c +@@ -0,0 +1,390 @@ ++/* ++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include "exfat_config.h" ++#include "exfat_global.h" ++ ++#include "exfat_nls.h" ++ ++UINT8 uni_upcase[NUM_UPCASE<<1] = { ++ 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, ++ 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0F, 0x00, ++ 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, ++ 0x18, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x1F, 0x00, ++ 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, ++ 0x28, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B, 0x00, 0x2C, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x2F, 0x00, ++ 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, ++ 0x38, 0x00, 0x39, 0x00, 0x3A, 0x00, 0x3B, 0x00, 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F, 0x00, ++ 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, ++ 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, ++ 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, ++ 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x5B, 0x00, 0x5C, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x5F, 0x00, ++ 0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, ++ 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, ++ 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, ++ 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x7B, 0x00, 0x7C, 0x00, 0x7D, 0x00, 0x7E, 0x00, 0x7F, 0x00, ++ 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, ++ 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00, 0x8C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00, ++ 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, ++ 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00, 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00, ++ 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, 0x00, ++ 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, 0x00, 0xAC, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAF, 0x00, ++ 0xB0, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB7, 0x00, ++ 0xB8, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBB, 0x00, 0xBC, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBF, 0x00, ++ 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, ++ 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, ++ 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD7, 0x00, ++ 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0xDF, 0x00, ++ 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, ++ 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, ++ 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xF7, 0x00, ++ 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0x78, 0x01, ++ 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01, ++ 0x08, 0x01, 0x08, 0x01, 0x0A, 0x01, 0x0A, 0x01, 0x0C, 0x01, 0x0C, 0x01, 0x0E, 0x01, 0x0E, 0x01, ++ 0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01, 0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01, ++ 0x18, 0x01, 0x18, 0x01, 0x1A, 0x01, 0x1A, 0x01, 0x1C, 0x01, 0x1C, 0x01, 0x1E, 0x01, 0x1E, 0x01, ++ 0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01, 0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01, ++ 0x28, 0x01, 0x28, 0x01, 0x2A, 0x01, 0x2A, 0x01, 0x2C, 0x01, 0x2C, 0x01, 0x2E, 0x01, 0x2E, 0x01, ++ 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01, 0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01, ++ 0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3B, 0x01, 0x3B, 0x01, 0x3D, 0x01, 0x3D, 0x01, 0x3F, 0x01, ++ 0x3F, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01, 0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01, ++ 0x47, 0x01, 0x49, 0x01, 0x4A, 0x01, 0x4A, 0x01, 0x4C, 0x01, 0x4C, 0x01, 0x4E, 0x01, 0x4E, 0x01, ++ 0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01, 0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01, ++ 0x58, 0x01, 0x58, 0x01, 0x5A, 0x01, 0x5A, 0x01, 0x5C, 0x01, 0x5C, 0x01, 0x5E, 0x01, 0x5E, 0x01, ++ 0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01, 0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01, ++ 0x68, 0x01, 0x68, 0x01, 0x6A, 0x01, 0x6A, 0x01, 0x6C, 0x01, 0x6C, 0x01, 0x6E, 0x01, 0x6E, 0x01, ++ 0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01, 0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01, ++ 0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7B, 0x01, 0x7B, 0x01, 0x7D, 0x01, 0x7D, 0x01, 0x7F, 0x01, ++ 0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01, 0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01, ++ 0x87, 0x01, 0x89, 0x01, 0x8A, 0x01, 0x8B, 0x01, 0x8B, 0x01, 0x8D, 0x01, 0x8E, 0x01, 0x8F, 0x01, ++ 0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01, 0x94, 0x01, 0xF6, 0x01, 0x96, 0x01, 0x97, 0x01, ++ 0x98, 0x01, 0x98, 0x01, 0x3D, 0x02, 0x9B, 0x01, 0x9C, 0x01, 0x9D, 0x01, 0x20, 0x02, 0x9F, 0x01, ++ 0xA0, 0x01, 0xA0, 0x01, 0xA2, 0x01, 0xA2, 0x01, 0xA4, 0x01, 0xA4, 0x01, 0xA6, 0x01, 0xA7, 0x01, ++ 0xA7, 0x01, 0xA9, 0x01, 0xAA, 0x01, 0xAB, 0x01, 0xAC, 0x01, 0xAC, 0x01, 0xAE, 0x01, 0xAF, 0x01, ++ 0xAF, 0x01, 0xB1, 0x01, 0xB2, 0x01, 0xB3, 0x01, 0xB3, 0x01, 0xB5, 0x01, 0xB5, 0x01, 0xB7, 0x01, ++ 0xB8, 0x01, 0xB8, 0x01, 0xBA, 0x01, 0xBB, 0x01, 0xBC, 0x01, 0xBC, 0x01, 0xBE, 0x01, 0xF7, 0x01, ++ 0xC0, 0x01, 0xC1, 0x01, 0xC2, 0x01, 0xC3, 0x01, 0xC4, 0x01, 0xC5, 0x01, 0xC4, 0x01, 0xC7, 0x01, ++ 0xC8, 0x01, 0xC7, 0x01, 0xCA, 0x01, 0xCB, 0x01, 0xCA, 0x01, 0xCD, 0x01, 0xCD, 0x01, 0xCF, 0x01, ++ 0xCF, 0x01, 0xD1, 0x01, 0xD1, 0x01, 0xD3, 0x01, 0xD3, 0x01, 0xD5, 0x01, 0xD5, 0x01, 0xD7, 0x01, ++ 0xD7, 0x01, 0xD9, 0x01, 0xD9, 0x01, 0xDB, 0x01, 0xDB, 0x01, 0x8E, 0x01, 0xDE, 0x01, 0xDE, 0x01, ++ 0xE0, 0x01, 0xE0, 0x01, 0xE2, 0x01, 0xE2, 0x01, 0xE4, 0x01, 0xE4, 0x01, 0xE6, 0x01, 0xE6, 0x01, ++ 0xE8, 0x01, 0xE8, 0x01, 0xEA, 0x01, 0xEA, 0x01, 0xEC, 0x01, 0xEC, 0x01, 0xEE, 0x01, 0xEE, 0x01, ++ 0xF0, 0x01, 0xF1, 0x01, 0xF2, 0x01, 0xF1, 0x01, 0xF4, 0x01, 0xF4, 0x01, 0xF6, 0x01, 0xF7, 0x01, ++ 0xF8, 0x01, 0xF8, 0x01, 0xFA, 0x01, 0xFA, 0x01, 0xFC, 0x01, 0xFC, 0x01, 0xFE, 0x01, 0xFE, 0x01, ++ 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02, ++ 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x0A, 0x02, 0x0C, 0x02, 0x0C, 0x02, 0x0E, 0x02, 0x0E, 0x02, ++ 0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02, 0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02, ++ 0x18, 0x02, 0x18, 0x02, 0x1A, 0x02, 0x1A, 0x02, 0x1C, 0x02, 0x1C, 0x02, 0x1E, 0x02, 0x1E, 0x02, ++ 0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02, 0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02, ++ 0x28, 0x02, 0x28, 0x02, 0x2A, 0x02, 0x2A, 0x02, 0x2C, 0x02, 0x2C, 0x02, 0x2E, 0x02, 0x2E, 0x02, ++ 0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02, 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02, ++ 0x38, 0x02, 0x39, 0x02, 0x65, 0x2C, 0x3B, 0x02, 0x3B, 0x02, 0x3D, 0x02, 0x66, 0x2C, 0x3F, 0x02, ++ 0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02, 0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02, ++ 0x48, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x4A, 0x02, 0x4C, 0x02, 0x4C, 0x02, 0x4E, 0x02, 0x4E, 0x02, ++ 0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01, 0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8A, 0x01, ++ 0x58, 0x02, 0x8F, 0x01, 0x5A, 0x02, 0x90, 0x01, 0x5C, 0x02, 0x5D, 0x02, 0x5E, 0x02, 0x5F, 0x02, ++ 0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01, 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02, ++ 0x97, 0x01, 0x96, 0x01, 0x6A, 0x02, 0x62, 0x2C, 0x6C, 0x02, 0x6D, 0x02, 0x6E, 0x02, 0x9C, 0x01, ++ 0x70, 0x02, 0x71, 0x02, 0x9D, 0x01, 0x73, 0x02, 0x74, 0x02, 0x9F, 0x01, 0x76, 0x02, 0x77, 0x02, ++ 0x78, 0x02, 0x79, 0x02, 0x7A, 0x02, 0x7B, 0x02, 0x7C, 0x02, 0x64, 0x2C, 0x7E, 0x02, 0x7F, 0x02, ++ 0xA6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xA9, 0x01, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02, ++ 0xAE, 0x01, 0x44, 0x02, 0xB1, 0x01, 0xB2, 0x01, 0x45, 0x02, 0x8D, 0x02, 0x8E, 0x02, 0x8F, 0x02, ++ 0x90, 0x02, 0x91, 0x02, 0xB7, 0x01, 0x93, 0x02, 0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02, ++ 0x98, 0x02, 0x99, 0x02, 0x9A, 0x02, 0x9B, 0x02, 0x9C, 0x02, 0x9D, 0x02, 0x9E, 0x02, 0x9F, 0x02, ++ 0xA0, 0x02, 0xA1, 0x02, 0xA2, 0x02, 0xA3, 0x02, 0xA4, 0x02, 0xA5, 0x02, 0xA6, 0x02, 0xA7, 0x02, ++ 0xA8, 0x02, 0xA9, 0x02, 0xAA, 0x02, 0xAB, 0x02, 0xAC, 0x02, 0xAD, 0x02, 0xAE, 0x02, 0xAF, 0x02, ++ 0xB0, 0x02, 0xB1, 0x02, 0xB2, 0x02, 0xB3, 0x02, 0xB4, 0x02, 0xB5, 0x02, 0xB6, 0x02, 0xB7, 0x02, ++ 0xB8, 0x02, 0xB9, 0x02, 0xBA, 0x02, 0xBB, 0x02, 0xBC, 0x02, 0xBD, 0x02, 0xBE, 0x02, 0xBF, 0x02, ++ 0xC0, 0x02, 0xC1, 0x02, 0xC2, 0x02, 0xC3, 0x02, 0xC4, 0x02, 0xC5, 0x02, 0xC6, 0x02, 0xC7, 0x02, ++ 0xC8, 0x02, 0xC9, 0x02, 0xCA, 0x02, 0xCB, 0x02, 0xCC, 0x02, 0xCD, 0x02, 0xCE, 0x02, 0xCF, 0x02, ++ 0xD0, 0x02, 0xD1, 0x02, 0xD2, 0x02, 0xD3, 0x02, 0xD4, 0x02, 0xD5, 0x02, 0xD6, 0x02, 0xD7, 0x02, ++ 0xD8, 0x02, 0xD9, 0x02, 0xDA, 0x02, 0xDB, 0x02, 0xDC, 0x02, 0xDD, 0x02, 0xDE, 0x02, 0xDF, 0x02, ++ 0xE0, 0x02, 0xE1, 0x02, 0xE2, 0x02, 0xE3, 0x02, 0xE4, 0x02, 0xE5, 0x02, 0xE6, 0x02, 0xE7, 0x02, ++ 0xE8, 0x02, 0xE9, 0x02, 0xEA, 0x02, 0xEB, 0x02, 0xEC, 0x02, 0xED, 0x02, 0xEE, 0x02, 0xEF, 0x02, ++ 0xF0, 0x02, 0xF1, 0x02, 0xF2, 0x02, 0xF3, 0x02, 0xF4, 0x02, 0xF5, 0x02, 0xF6, 0x02, 0xF7, 0x02, ++ 0xF8, 0x02, 0xF9, 0x02, 0xFA, 0x02, 0xFB, 0x02, 0xFC, 0x02, 0xFD, 0x02, 0xFE, 0x02, 0xFF, 0x02, ++ 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03, ++ 0x08, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0B, 0x03, 0x0C, 0x03, 0x0D, 0x03, 0x0E, 0x03, 0x0F, 0x03, ++ 0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03, 0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03, ++ 0x18, 0x03, 0x19, 0x03, 0x1A, 0x03, 0x1B, 0x03, 0x1C, 0x03, 0x1D, 0x03, 0x1E, 0x03, 0x1F, 0x03, ++ 0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03, 0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03, ++ 0x28, 0x03, 0x29, 0x03, 0x2A, 0x03, 0x2B, 0x03, 0x2C, 0x03, 0x2D, 0x03, 0x2E, 0x03, 0x2F, 0x03, ++ 0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03, 0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03, ++ 0x38, 0x03, 0x39, 0x03, 0x3A, 0x03, 0x3B, 0x03, 0x3C, 0x03, 0x3D, 0x03, 0x3E, 0x03, 0x3F, 0x03, ++ 0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03, 0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03, ++ 0x48, 0x03, 0x49, 0x03, 0x4A, 0x03, 0x4B, 0x03, 0x4C, 0x03, 0x4D, 0x03, 0x4E, 0x03, 0x4F, 0x03, ++ 0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03, 0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03, ++ 0x58, 0x03, 0x59, 0x03, 0x5A, 0x03, 0x5B, 0x03, 0x5C, 0x03, 0x5D, 0x03, 0x5E, 0x03, 0x5F, 0x03, ++ 0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03, 0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03, ++ 0x68, 0x03, 0x69, 0x03, 0x6A, 0x03, 0x6B, 0x03, 0x6C, 0x03, 0x6D, 0x03, 0x6E, 0x03, 0x6F, 0x03, ++ 0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03, 0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03, ++ 0x78, 0x03, 0x79, 0x03, 0x7A, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, 0x7E, 0x03, 0x7F, 0x03, ++ 0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03, 0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03, ++ 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, 0x8B, 0x03, 0x8C, 0x03, 0x8D, 0x03, 0x8E, 0x03, 0x8F, 0x03, ++ 0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, ++ 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, ++ 0xA0, 0x03, 0xA1, 0x03, 0xA2, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, ++ 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, ++ 0xB0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, ++ 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, ++ 0xA0, 0x03, 0xA1, 0x03, 0xA3, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, ++ 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x8C, 0x03, 0x8E, 0x03, 0x8F, 0x03, 0xCF, 0x03, ++ 0xD0, 0x03, 0xD1, 0x03, 0xD2, 0x03, 0xD3, 0x03, 0xD4, 0x03, 0xD5, 0x03, 0xD6, 0x03, 0xD7, 0x03, ++ 0xD8, 0x03, 0xD8, 0x03, 0xDA, 0x03, 0xDA, 0x03, 0xDC, 0x03, 0xDC, 0x03, 0xDE, 0x03, 0xDE, 0x03, ++ 0xE0, 0x03, 0xE0, 0x03, 0xE2, 0x03, 0xE2, 0x03, 0xE4, 0x03, 0xE4, 0x03, 0xE6, 0x03, 0xE6, 0x03, ++ 0xE8, 0x03, 0xE8, 0x03, 0xEA, 0x03, 0xEA, 0x03, 0xEC, 0x03, 0xEC, 0x03, 0xEE, 0x03, 0xEE, 0x03, ++ 0xF0, 0x03, 0xF1, 0x03, 0xF9, 0x03, 0xF3, 0x03, 0xF4, 0x03, 0xF5, 0x03, 0xF6, 0x03, 0xF7, 0x03, ++ 0xF7, 0x03, 0xF9, 0x03, 0xFA, 0x03, 0xFA, 0x03, 0xFC, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, ++ 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, ++ 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, ++ 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, ++ 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, ++ 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, ++ 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, ++ 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, ++ 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, ++ 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, ++ 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, ++ 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, ++ 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, ++ 0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04, 0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04, ++ 0x68, 0x04, 0x68, 0x04, 0x6A, 0x04, 0x6A, 0x04, 0x6C, 0x04, 0x6C, 0x04, 0x6E, 0x04, 0x6E, 0x04, ++ 0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04, 0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04, ++ 0x78, 0x04, 0x78, 0x04, 0x7A, 0x04, 0x7A, 0x04, 0x7C, 0x04, 0x7C, 0x04, 0x7E, 0x04, 0x7E, 0x04, ++ 0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04, 0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04, ++ 0x88, 0x04, 0x89, 0x04, 0x8A, 0x04, 0x8A, 0x04, 0x8C, 0x04, 0x8C, 0x04, 0x8E, 0x04, 0x8E, 0x04, ++ 0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04, 0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04, ++ 0x98, 0x04, 0x98, 0x04, 0x9A, 0x04, 0x9A, 0x04, 0x9C, 0x04, 0x9C, 0x04, 0x9E, 0x04, 0x9E, 0x04, ++ 0xA0, 0x04, 0xA0, 0x04, 0xA2, 0x04, 0xA2, 0x04, 0xA4, 0x04, 0xA4, 0x04, 0xA6, 0x04, 0xA6, 0x04, ++ 0xA8, 0x04, 0xA8, 0x04, 0xAA, 0x04, 0xAA, 0x04, 0xAC, 0x04, 0xAC, 0x04, 0xAE, 0x04, 0xAE, 0x04, ++ 0xB0, 0x04, 0xB0, 0x04, 0xB2, 0x04, 0xB2, 0x04, 0xB4, 0x04, 0xB4, 0x04, 0xB6, 0x04, 0xB6, 0x04, ++ 0xB8, 0x04, 0xB8, 0x04, 0xBA, 0x04, 0xBA, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBE, 0x04, 0xBE, 0x04, ++ 0xC0, 0x04, 0xC1, 0x04, 0xC1, 0x04, 0xC3, 0x04, 0xC3, 0x04, 0xC5, 0x04, 0xC5, 0x04, 0xC7, 0x04, ++ 0xC7, 0x04, 0xC9, 0x04, 0xC9, 0x04, 0xCB, 0x04, 0xCB, 0x04, 0xCD, 0x04, 0xCD, 0x04, 0xC0, 0x04, ++ 0xD0, 0x04, 0xD0, 0x04, 0xD2, 0x04, 0xD2, 0x04, 0xD4, 0x04, 0xD4, 0x04, 0xD6, 0x04, 0xD6, 0x04, ++ 0xD8, 0x04, 0xD8, 0x04, 0xDA, 0x04, 0xDA, 0x04, 0xDC, 0x04, 0xDC, 0x04, 0xDE, 0x04, 0xDE, 0x04, ++ 0xE0, 0x04, 0xE0, 0x04, 0xE2, 0x04, 0xE2, 0x04, 0xE4, 0x04, 0xE4, 0x04, 0xE6, 0x04, 0xE6, 0x04, ++ 0xE8, 0x04, 0xE8, 0x04, 0xEA, 0x04, 0xEA, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEE, 0x04, 0xEE, 0x04, ++ 0xF0, 0x04, 0xF0, 0x04, 0xF2, 0x04, 0xF2, 0x04, 0xF4, 0x04, 0xF4, 0x04, 0xF6, 0x04, 0xF6, 0x04, ++ 0xF8, 0x04, 0xF8, 0x04, 0xFA, 0x04, 0xFA, 0x04, 0xFC, 0x04, 0xFC, 0x04, 0xFE, 0x04, 0xFE, 0x04, ++ 0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, ++ 0x08, 0x05, 0x08, 0x05, 0x0A, 0x05, 0x0A, 0x05, 0x0C, 0x05, 0x0C, 0x05, 0x0E, 0x05, 0x0E, 0x05, ++ 0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05, 0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05, ++ 0x18, 0x05, 0x19, 0x05, 0x1A, 0x05, 0x1B, 0x05, 0x1C, 0x05, 0x1D, 0x05, 0x1E, 0x05, 0x1F, 0x05, ++ 0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05, 0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05, ++ 0x28, 0x05, 0x29, 0x05, 0x2A, 0x05, 0x2B, 0x05, 0x2C, 0x05, 0x2D, 0x05, 0x2E, 0x05, 0x2F, 0x05, ++ 0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, ++ 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, ++ 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, ++ 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, ++ 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05, ++ 0x58, 0x05, 0x59, 0x05, 0x5A, 0x05, 0x5B, 0x05, 0x5C, 0x05, 0x5D, 0x05, 0x5E, 0x05, 0x5F, 0x05, ++ 0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, ++ 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, ++ 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, ++ 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, ++ 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xFF, 0xFF, ++ 0xF6, 0x17, 0x63, 0x2C, 0x7E, 0x1D, 0x7F, 0x1D, 0x80, 0x1D, 0x81, 0x1D, 0x82, 0x1D, 0x83, 0x1D, ++ 0x84, 0x1D, 0x85, 0x1D, 0x86, 0x1D, 0x87, 0x1D, 0x88, 0x1D, 0x89, 0x1D, 0x8A, 0x1D, 0x8B, 0x1D, ++ 0x8C, 0x1D, 0x8D, 0x1D, 0x8E, 0x1D, 0x8F, 0x1D, 0x90, 0x1D, 0x91, 0x1D, 0x92, 0x1D, 0x93, 0x1D, ++ 0x94, 0x1D, 0x95, 0x1D, 0x96, 0x1D, 0x97, 0x1D, 0x98, 0x1D, 0x99, 0x1D, 0x9A, 0x1D, 0x9B, 0x1D, ++ 0x9C, 0x1D, 0x9D, 0x1D, 0x9E, 0x1D, 0x9F, 0x1D, 0xA0, 0x1D, 0xA1, 0x1D, 0xA2, 0x1D, 0xA3, 0x1D, ++ 0xA4, 0x1D, 0xA5, 0x1D, 0xA6, 0x1D, 0xA7, 0x1D, 0xA8, 0x1D, 0xA9, 0x1D, 0xAA, 0x1D, 0xAB, 0x1D, ++ 0xAC, 0x1D, 0xAD, 0x1D, 0xAE, 0x1D, 0xAF, 0x1D, 0xB0, 0x1D, 0xB1, 0x1D, 0xB2, 0x1D, 0xB3, 0x1D, ++ 0xB4, 0x1D, 0xB5, 0x1D, 0xB6, 0x1D, 0xB7, 0x1D, 0xB8, 0x1D, 0xB9, 0x1D, 0xBA, 0x1D, 0xBB, 0x1D, ++ 0xBC, 0x1D, 0xBD, 0x1D, 0xBE, 0x1D, 0xBF, 0x1D, 0xC0, 0x1D, 0xC1, 0x1D, 0xC2, 0x1D, 0xC3, 0x1D, ++ 0xC4, 0x1D, 0xC5, 0x1D, 0xC6, 0x1D, 0xC7, 0x1D, 0xC8, 0x1D, 0xC9, 0x1D, 0xCA, 0x1D, 0xCB, 0x1D, ++ 0xCC, 0x1D, 0xCD, 0x1D, 0xCE, 0x1D, 0xCF, 0x1D, 0xD0, 0x1D, 0xD1, 0x1D, 0xD2, 0x1D, 0xD3, 0x1D, ++ 0xD4, 0x1D, 0xD5, 0x1D, 0xD6, 0x1D, 0xD7, 0x1D, 0xD8, 0x1D, 0xD9, 0x1D, 0xDA, 0x1D, 0xDB, 0x1D, ++ 0xDC, 0x1D, 0xDD, 0x1D, 0xDE, 0x1D, 0xDF, 0x1D, 0xE0, 0x1D, 0xE1, 0x1D, 0xE2, 0x1D, 0xE3, 0x1D, ++ 0xE4, 0x1D, 0xE5, 0x1D, 0xE6, 0x1D, 0xE7, 0x1D, 0xE8, 0x1D, 0xE9, 0x1D, 0xEA, 0x1D, 0xEB, 0x1D, ++ 0xEC, 0x1D, 0xED, 0x1D, 0xEE, 0x1D, 0xEF, 0x1D, 0xF0, 0x1D, 0xF1, 0x1D, 0xF2, 0x1D, 0xF3, 0x1D, ++ 0xF4, 0x1D, 0xF5, 0x1D, 0xF6, 0x1D, 0xF7, 0x1D, 0xF8, 0x1D, 0xF9, 0x1D, 0xFA, 0x1D, 0xFB, 0x1D, ++ 0xFC, 0x1D, 0xFD, 0x1D, 0xFE, 0x1D, 0xFF, 0x1D, 0x00, 0x1E, 0x00, 0x1E, 0x02, 0x1E, 0x02, 0x1E, ++ 0x04, 0x1E, 0x04, 0x1E, 0x06, 0x1E, 0x06, 0x1E, 0x08, 0x1E, 0x08, 0x1E, 0x0A, 0x1E, 0x0A, 0x1E, ++ 0x0C, 0x1E, 0x0C, 0x1E, 0x0E, 0x1E, 0x0E, 0x1E, 0x10, 0x1E, 0x10, 0x1E, 0x12, 0x1E, 0x12, 0x1E, ++ 0x14, 0x1E, 0x14, 0x1E, 0x16, 0x1E, 0x16, 0x1E, 0x18, 0x1E, 0x18, 0x1E, 0x1A, 0x1E, 0x1A, 0x1E, ++ 0x1C, 0x1E, 0x1C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x20, 0x1E, 0x20, 0x1E, 0x22, 0x1E, 0x22, 0x1E, ++ 0x24, 0x1E, 0x24, 0x1E, 0x26, 0x1E, 0x26, 0x1E, 0x28, 0x1E, 0x28, 0x1E, 0x2A, 0x1E, 0x2A, 0x1E, ++ 0x2C, 0x1E, 0x2C, 0x1E, 0x2E, 0x1E, 0x2E, 0x1E, 0x30, 0x1E, 0x30, 0x1E, 0x32, 0x1E, 0x32, 0x1E, ++ 0x34, 0x1E, 0x34, 0x1E, 0x36, 0x1E, 0x36, 0x1E, 0x38, 0x1E, 0x38, 0x1E, 0x3A, 0x1E, 0x3A, 0x1E, ++ 0x3C, 0x1E, 0x3C, 0x1E, 0x3E, 0x1E, 0x3E, 0x1E, 0x40, 0x1E, 0x40, 0x1E, 0x42, 0x1E, 0x42, 0x1E, ++ 0x44, 0x1E, 0x44, 0x1E, 0x46, 0x1E, 0x46, 0x1E, 0x48, 0x1E, 0x48, 0x1E, 0x4A, 0x1E, 0x4A, 0x1E, ++ 0x4C, 0x1E, 0x4C, 0x1E, 0x4E, 0x1E, 0x4E, 0x1E, 0x50, 0x1E, 0x50, 0x1E, 0x52, 0x1E, 0x52, 0x1E, ++ 0x54, 0x1E, 0x54, 0x1E, 0x56, 0x1E, 0x56, 0x1E, 0x58, 0x1E, 0x58, 0x1E, 0x5A, 0x1E, 0x5A, 0x1E, ++ 0x5C, 0x1E, 0x5C, 0x1E, 0x5E, 0x1E, 0x5E, 0x1E, 0x60, 0x1E, 0x60, 0x1E, 0x62, 0x1E, 0x62, 0x1E, ++ 0x64, 0x1E, 0x64, 0x1E, 0x66, 0x1E, 0x66, 0x1E, 0x68, 0x1E, 0x68, 0x1E, 0x6A, 0x1E, 0x6A, 0x1E, ++ 0x6C, 0x1E, 0x6C, 0x1E, 0x6E, 0x1E, 0x6E, 0x1E, 0x70, 0x1E, 0x70, 0x1E, 0x72, 0x1E, 0x72, 0x1E, ++ 0x74, 0x1E, 0x74, 0x1E, 0x76, 0x1E, 0x76, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x7A, 0x1E, 0x7A, 0x1E, ++ 0x7C, 0x1E, 0x7C, 0x1E, 0x7E, 0x1E, 0x7E, 0x1E, 0x80, 0x1E, 0x80, 0x1E, 0x82, 0x1E, 0x82, 0x1E, ++ 0x84, 0x1E, 0x84, 0x1E, 0x86, 0x1E, 0x86, 0x1E, 0x88, 0x1E, 0x88, 0x1E, 0x8A, 0x1E, 0x8A, 0x1E, ++ 0x8C, 0x1E, 0x8C, 0x1E, 0x8E, 0x1E, 0x8E, 0x1E, 0x90, 0x1E, 0x90, 0x1E, 0x92, 0x1E, 0x92, 0x1E, ++ 0x94, 0x1E, 0x94, 0x1E, 0x96, 0x1E, 0x97, 0x1E, 0x98, 0x1E, 0x99, 0x1E, 0x9A, 0x1E, 0x9B, 0x1E, ++ 0x9C, 0x1E, 0x9D, 0x1E, 0x9E, 0x1E, 0x9F, 0x1E, 0xA0, 0x1E, 0xA0, 0x1E, 0xA2, 0x1E, 0xA2, 0x1E, ++ 0xA4, 0x1E, 0xA4, 0x1E, 0xA6, 0x1E, 0xA6, 0x1E, 0xA8, 0x1E, 0xA8, 0x1E, 0xAA, 0x1E, 0xAA, 0x1E, ++ 0xAC, 0x1E, 0xAC, 0x1E, 0xAE, 0x1E, 0xAE, 0x1E, 0xB0, 0x1E, 0xB0, 0x1E, 0xB2, 0x1E, 0xB2, 0x1E, ++ 0xB4, 0x1E, 0xB4, 0x1E, 0xB6, 0x1E, 0xB6, 0x1E, 0xB8, 0x1E, 0xB8, 0x1E, 0xBA, 0x1E, 0xBA, 0x1E, ++ 0xBC, 0x1E, 0xBC, 0x1E, 0xBE, 0x1E, 0xBE, 0x1E, 0xC0, 0x1E, 0xC0, 0x1E, 0xC2, 0x1E, 0xC2, 0x1E, ++ 0xC4, 0x1E, 0xC4, 0x1E, 0xC6, 0x1E, 0xC6, 0x1E, 0xC8, 0x1E, 0xC8, 0x1E, 0xCA, 0x1E, 0xCA, 0x1E, ++ 0xCC, 0x1E, 0xCC, 0x1E, 0xCE, 0x1E, 0xCE, 0x1E, 0xD0, 0x1E, 0xD0, 0x1E, 0xD2, 0x1E, 0xD2, 0x1E, ++ 0xD4, 0x1E, 0xD4, 0x1E, 0xD6, 0x1E, 0xD6, 0x1E, 0xD8, 0x1E, 0xD8, 0x1E, 0xDA, 0x1E, 0xDA, 0x1E, ++ 0xDC, 0x1E, 0xDC, 0x1E, 0xDE, 0x1E, 0xDE, 0x1E, 0xE0, 0x1E, 0xE0, 0x1E, 0xE2, 0x1E, 0xE2, 0x1E, ++ 0xE4, 0x1E, 0xE4, 0x1E, 0xE6, 0x1E, 0xE6, 0x1E, 0xE8, 0x1E, 0xE8, 0x1E, 0xEA, 0x1E, 0xEA, 0x1E, ++ 0xEC, 0x1E, 0xEC, 0x1E, 0xEE, 0x1E, 0xEE, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF2, 0x1E, 0xF2, 0x1E, ++ 0xF4, 0x1E, 0xF4, 0x1E, 0xF6, 0x1E, 0xF6, 0x1E, 0xF8, 0x1E, 0xF8, 0x1E, 0xFA, 0x1E, 0xFB, 0x1E, ++ 0xFC, 0x1E, 0xFD, 0x1E, 0xFE, 0x1E, 0xFF, 0x1E, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, ++ 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, ++ 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, ++ 0x1C, 0x1F, 0x1D, 0x1F, 0x16, 0x1F, 0x17, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, ++ 0x1C, 0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, ++ 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, ++ 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, ++ 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, ++ 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, ++ 0x4C, 0x1F, 0x4D, 0x1F, 0x46, 0x1F, 0x47, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, ++ 0x4C, 0x1F, 0x4D, 0x1F, 0x4E, 0x1F, 0x4F, 0x1F, 0x50, 0x1F, 0x59, 0x1F, 0x52, 0x1F, 0x5B, 0x1F, ++ 0x54, 0x1F, 0x5D, 0x1F, 0x56, 0x1F, 0x5F, 0x1F, 0x58, 0x1F, 0x59, 0x1F, 0x5A, 0x1F, 0x5B, 0x1F, ++ 0x5C, 0x1F, 0x5D, 0x1F, 0x5E, 0x1F, 0x5F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, ++ 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, ++ 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, ++ 0xCA, 0x1F, 0xCB, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, ++ 0xFA, 0x1F, 0xFB, 0x1F, 0x7E, 0x1F, 0x7F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, ++ 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, ++ 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, ++ 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, ++ 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, ++ 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, ++ 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xB2, 0x1F, 0xBC, 0x1F, ++ 0xB4, 0x1F, 0xB5, 0x1F, 0xB6, 0x1F, 0xB7, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, ++ 0xBC, 0x1F, 0xBD, 0x1F, 0xBE, 0x1F, 0xBF, 0x1F, 0xC0, 0x1F, 0xC1, 0x1F, 0xC2, 0x1F, 0xC3, 0x1F, ++ 0xC4, 0x1F, 0xC5, 0x1F, 0xC6, 0x1F, 0xC7, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, 0xCA, 0x1F, 0xCB, 0x1F, ++ 0xC3, 0x1F, 0xCD, 0x1F, 0xCE, 0x1F, 0xCF, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xD2, 0x1F, 0xD3, 0x1F, ++ 0xD4, 0x1F, 0xD5, 0x1F, 0xD6, 0x1F, 0xD7, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, ++ 0xDC, 0x1F, 0xDD, 0x1F, 0xDE, 0x1F, 0xDF, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xE2, 0x1F, 0xE3, 0x1F, ++ 0xE4, 0x1F, 0xEC, 0x1F, 0xE6, 0x1F, 0xE7, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, ++ 0xEC, 0x1F, 0xED, 0x1F, 0xEE, 0x1F, 0xEF, 0x1F, 0xF0, 0x1F, 0xF1, 0x1F, 0xF2, 0x1F, 0xF3, 0x1F, ++ 0xF4, 0x1F, 0xF5, 0x1F, 0xF6, 0x1F, 0xF7, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xFA, 0x1F, 0xFB, 0x1F, ++ 0xF3, 0x1F, 0xFD, 0x1F, 0xFE, 0x1F, 0xFF, 0x1F, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20, ++ 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x09, 0x20, 0x0A, 0x20, 0x0B, 0x20, ++ 0x0C, 0x20, 0x0D, 0x20, 0x0E, 0x20, 0x0F, 0x20, 0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20, ++ 0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20, 0x18, 0x20, 0x19, 0x20, 0x1A, 0x20, 0x1B, 0x20, ++ 0x1C, 0x20, 0x1D, 0x20, 0x1E, 0x20, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20, ++ 0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20, 0x28, 0x20, 0x29, 0x20, 0x2A, 0x20, 0x2B, 0x20, ++ 0x2C, 0x20, 0x2D, 0x20, 0x2E, 0x20, 0x2F, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20, ++ 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20, 0x38, 0x20, 0x39, 0x20, 0x3A, 0x20, 0x3B, 0x20, ++ 0x3C, 0x20, 0x3D, 0x20, 0x3E, 0x20, 0x3F, 0x20, 0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20, ++ 0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20, 0x48, 0x20, 0x49, 0x20, 0x4A, 0x20, 0x4B, 0x20, ++ 0x4C, 0x20, 0x4D, 0x20, 0x4E, 0x20, 0x4F, 0x20, 0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20, ++ 0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20, 0x58, 0x20, 0x59, 0x20, 0x5A, 0x20, 0x5B, 0x20, ++ 0x5C, 0x20, 0x5D, 0x20, 0x5E, 0x20, 0x5F, 0x20, 0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20, ++ 0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20, 0x68, 0x20, 0x69, 0x20, 0x6A, 0x20, 0x6B, 0x20, ++ 0x6C, 0x20, 0x6D, 0x20, 0x6E, 0x20, 0x6F, 0x20, 0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20, ++ 0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20, 0x78, 0x20, 0x79, 0x20, 0x7A, 0x20, 0x7B, 0x20, ++ 0x7C, 0x20, 0x7D, 0x20, 0x7E, 0x20, 0x7F, 0x20, 0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20, ++ 0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x8A, 0x20, 0x8B, 0x20, ++ 0x8C, 0x20, 0x8D, 0x20, 0x8E, 0x20, 0x8F, 0x20, 0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20, ++ 0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20, 0x98, 0x20, 0x99, 0x20, 0x9A, 0x20, 0x9B, 0x20, ++ 0x9C, 0x20, 0x9D, 0x20, 0x9E, 0x20, 0x9F, 0x20, 0xA0, 0x20, 0xA1, 0x20, 0xA2, 0x20, 0xA3, 0x20, ++ 0xA4, 0x20, 0xA5, 0x20, 0xA6, 0x20, 0xA7, 0x20, 0xA8, 0x20, 0xA9, 0x20, 0xAA, 0x20, 0xAB, 0x20, ++ 0xAC, 0x20, 0xAD, 0x20, 0xAE, 0x20, 0xAF, 0x20, 0xB0, 0x20, 0xB1, 0x20, 0xB2, 0x20, 0xB3, 0x20, ++ 0xB4, 0x20, 0xB5, 0x20, 0xB6, 0x20, 0xB7, 0x20, 0xB8, 0x20, 0xB9, 0x20, 0xBA, 0x20, 0xBB, 0x20, ++ 0xBC, 0x20, 0xBD, 0x20, 0xBE, 0x20, 0xBF, 0x20, 0xC0, 0x20, 0xC1, 0x20, 0xC2, 0x20, 0xC3, 0x20, ++ 0xC4, 0x20, 0xC5, 0x20, 0xC6, 0x20, 0xC7, 0x20, 0xC8, 0x20, 0xC9, 0x20, 0xCA, 0x20, 0xCB, 0x20, ++ 0xCC, 0x20, 0xCD, 0x20, 0xCE, 0x20, 0xCF, 0x20, 0xD0, 0x20, 0xD1, 0x20, 0xD2, 0x20, 0xD3, 0x20, ++ 0xD4, 0x20, 0xD5, 0x20, 0xD6, 0x20, 0xD7, 0x20, 0xD8, 0x20, 0xD9, 0x20, 0xDA, 0x20, 0xDB, 0x20, ++ 0xDC, 0x20, 0xDD, 0x20, 0xDE, 0x20, 0xDF, 0x20, 0xE0, 0x20, 0xE1, 0x20, 0xE2, 0x20, 0xE3, 0x20, ++ 0xE4, 0x20, 0xE5, 0x20, 0xE6, 0x20, 0xE7, 0x20, 0xE8, 0x20, 0xE9, 0x20, 0xEA, 0x20, 0xEB, 0x20, ++ 0xEC, 0x20, 0xED, 0x20, 0xEE, 0x20, 0xEF, 0x20, 0xF0, 0x20, 0xF1, 0x20, 0xF2, 0x20, 0xF3, 0x20, ++ 0xF4, 0x20, 0xF5, 0x20, 0xF6, 0x20, 0xF7, 0x20, 0xF8, 0x20, 0xF9, 0x20, 0xFA, 0x20, 0xFB, 0x20, ++ 0xFC, 0x20, 0xFD, 0x20, 0xFE, 0x20, 0xFF, 0x20, 0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21, ++ 0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21, 0x08, 0x21, 0x09, 0x21, 0x0A, 0x21, 0x0B, 0x21, ++ 0x0C, 0x21, 0x0D, 0x21, 0x0E, 0x21, 0x0F, 0x21, 0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21, ++ 0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21, 0x18, 0x21, 0x19, 0x21, 0x1A, 0x21, 0x1B, 0x21, ++ 0x1C, 0x21, 0x1D, 0x21, 0x1E, 0x21, 0x1F, 0x21, 0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21, ++ 0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21, 0x28, 0x21, 0x29, 0x21, 0x2A, 0x21, 0x2B, 0x21, ++ 0x2C, 0x21, 0x2D, 0x21, 0x2E, 0x21, 0x2F, 0x21, 0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21, ++ 0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21, 0x38, 0x21, 0x39, 0x21, 0x3A, 0x21, 0x3B, 0x21, ++ 0x3C, 0x21, 0x3D, 0x21, 0x3E, 0x21, 0x3F, 0x21, 0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21, ++ 0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21, 0x48, 0x21, 0x49, 0x21, 0x4A, 0x21, 0x4B, 0x21, ++ 0x4C, 0x21, 0x4D, 0x21, 0x32, 0x21, 0x4F, 0x21, 0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21, ++ 0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21, 0x58, 0x21, 0x59, 0x21, 0x5A, 0x21, 0x5B, 0x21, ++ 0x5C, 0x21, 0x5D, 0x21, 0x5E, 0x21, 0x5F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, ++ 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, ++ 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, ++ 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, ++ 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21, ++ 0x83, 0x21, 0xFF, 0xFF, 0x4B, 0x03, 0xB6, 0x24, 0xB7, 0x24, 0xB8, 0x24, 0xB9, 0x24, 0xBA, 0x24, ++ 0xBB, 0x24, 0xBC, 0x24, 0xBD, 0x24, 0xBE, 0x24, 0xBF, 0x24, 0xC0, 0x24, 0xC1, 0x24, 0xC2, 0x24, ++ 0xC3, 0x24, 0xC4, 0x24, 0xC5, 0x24, 0xC6, 0x24, 0xC7, 0x24, 0xC8, 0x24, 0xC9, 0x24, 0xCA, 0x24, ++ 0xCB, 0x24, 0xCC, 0x24, 0xCD, 0x24, 0xCE, 0x24, 0xCF, 0x24, 0xFF, 0xFF, 0x46, 0x07, 0x00, 0x2C, ++ 0x01, 0x2C, 0x02, 0x2C, 0x03, 0x2C, 0x04, 0x2C, 0x05, 0x2C, 0x06, 0x2C, 0x07, 0x2C, 0x08, 0x2C, ++ 0x09, 0x2C, 0x0A, 0x2C, 0x0B, 0x2C, 0x0C, 0x2C, 0x0D, 0x2C, 0x0E, 0x2C, 0x0F, 0x2C, 0x10, 0x2C, ++ 0x11, 0x2C, 0x12, 0x2C, 0x13, 0x2C, 0x14, 0x2C, 0x15, 0x2C, 0x16, 0x2C, 0x17, 0x2C, 0x18, 0x2C, ++ 0x19, 0x2C, 0x1A, 0x2C, 0x1B, 0x2C, 0x1C, 0x2C, 0x1D, 0x2C, 0x1E, 0x2C, 0x1F, 0x2C, 0x20, 0x2C, ++ 0x21, 0x2C, 0x22, 0x2C, 0x23, 0x2C, 0x24, 0x2C, 0x25, 0x2C, 0x26, 0x2C, 0x27, 0x2C, 0x28, 0x2C, ++ 0x29, 0x2C, 0x2A, 0x2C, 0x2B, 0x2C, 0x2C, 0x2C, 0x2D, 0x2C, 0x2E, 0x2C, 0x5F, 0x2C, 0x60, 0x2C, ++ 0x60, 0x2C, 0x62, 0x2C, 0x63, 0x2C, 0x64, 0x2C, 0x65, 0x2C, 0x66, 0x2C, 0x67, 0x2C, 0x67, 0x2C, ++ 0x69, 0x2C, 0x69, 0x2C, 0x6B, 0x2C, 0x6B, 0x2C, 0x6D, 0x2C, 0x6E, 0x2C, 0x6F, 0x2C, 0x70, 0x2C, ++ 0x71, 0x2C, 0x72, 0x2C, 0x73, 0x2C, 0x74, 0x2C, 0x75, 0x2C, 0x75, 0x2C, 0x77, 0x2C, 0x78, 0x2C, ++ 0x79, 0x2C, 0x7A, 0x2C, 0x7B, 0x2C, 0x7C, 0x2C, 0x7D, 0x2C, 0x7E, 0x2C, 0x7F, 0x2C, 0x80, 0x2C, ++ 0x80, 0x2C, 0x82, 0x2C, 0x82, 0x2C, 0x84, 0x2C, 0x84, 0x2C, 0x86, 0x2C, 0x86, 0x2C, 0x88, 0x2C, ++ 0x88, 0x2C, 0x8A, 0x2C, 0x8A, 0x2C, 0x8C, 0x2C, 0x8C, 0x2C, 0x8E, 0x2C, 0x8E, 0x2C, 0x90, 0x2C, ++ 0x90, 0x2C, 0x92, 0x2C, 0x92, 0x2C, 0x94, 0x2C, 0x94, 0x2C, 0x96, 0x2C, 0x96, 0x2C, 0x98, 0x2C, ++ 0x98, 0x2C, 0x9A, 0x2C, 0x9A, 0x2C, 0x9C, 0x2C, 0x9C, 0x2C, 0x9E, 0x2C, 0x9E, 0x2C, 0xA0, 0x2C, ++ 0xA0, 0x2C, 0xA2, 0x2C, 0xA2, 0x2C, 0xA4, 0x2C, 0xA4, 0x2C, 0xA6, 0x2C, 0xA6, 0x2C, 0xA8, 0x2C, ++ 0xA8, 0x2C, 0xAA, 0x2C, 0xAA, 0x2C, 0xAC, 0x2C, 0xAC, 0x2C, 0xAE, 0x2C, 0xAE, 0x2C, 0xB0, 0x2C, ++ 0xB0, 0x2C, 0xB2, 0x2C, 0xB2, 0x2C, 0xB4, 0x2C, 0xB4, 0x2C, 0xB6, 0x2C, 0xB6, 0x2C, 0xB8, 0x2C, ++ 0xB8, 0x2C, 0xBA, 0x2C, 0xBA, 0x2C, 0xBC, 0x2C, 0xBC, 0x2C, 0xBE, 0x2C, 0xBE, 0x2C, 0xC0, 0x2C, ++ 0xC0, 0x2C, 0xC2, 0x2C, 0xC2, 0x2C, 0xC4, 0x2C, 0xC4, 0x2C, 0xC6, 0x2C, 0xC6, 0x2C, 0xC8, 0x2C, ++ 0xC8, 0x2C, 0xCA, 0x2C, 0xCA, 0x2C, 0xCC, 0x2C, 0xCC, 0x2C, 0xCE, 0x2C, 0xCE, 0x2C, 0xD0, 0x2C, ++ 0xD0, 0x2C, 0xD2, 0x2C, 0xD2, 0x2C, 0xD4, 0x2C, 0xD4, 0x2C, 0xD6, 0x2C, 0xD6, 0x2C, 0xD8, 0x2C, ++ 0xD8, 0x2C, 0xDA, 0x2C, 0xDA, 0x2C, 0xDC, 0x2C, 0xDC, 0x2C, 0xDE, 0x2C, 0xDE, 0x2C, 0xE0, 0x2C, ++ 0xE0, 0x2C, 0xE2, 0x2C, 0xE2, 0x2C, 0xE4, 0x2C, 0xE5, 0x2C, 0xE6, 0x2C, 0xE7, 0x2C, 0xE8, 0x2C, ++ 0xE9, 0x2C, 0xEA, 0x2C, 0xEB, 0x2C, 0xEC, 0x2C, 0xED, 0x2C, 0xEE, 0x2C, 0xEF, 0x2C, 0xF0, 0x2C, ++ 0xF1, 0x2C, 0xF2, 0x2C, 0xF3, 0x2C, 0xF4, 0x2C, 0xF5, 0x2C, 0xF6, 0x2C, 0xF7, 0x2C, 0xF8, 0x2C, ++ 0xF9, 0x2C, 0xFA, 0x2C, 0xFB, 0x2C, 0xFC, 0x2C, 0xFD, 0x2C, 0xFE, 0x2C, 0xFF, 0x2C, 0xA0, 0x10, ++ 0xA1, 0x10, 0xA2, 0x10, 0xA3, 0x10, 0xA4, 0x10, 0xA5, 0x10, 0xA6, 0x10, 0xA7, 0x10, 0xA8, 0x10, ++ 0xA9, 0x10, 0xAA, 0x10, 0xAB, 0x10, 0xAC, 0x10, 0xAD, 0x10, 0xAE, 0x10, 0xAF, 0x10, 0xB0, 0x10, ++ 0xB1, 0x10, 0xB2, 0x10, 0xB3, 0x10, 0xB4, 0x10, 0xB5, 0x10, 0xB6, 0x10, 0xB7, 0x10, 0xB8, 0x10, ++ 0xB9, 0x10, 0xBA, 0x10, 0xBB, 0x10, 0xBC, 0x10, 0xBD, 0x10, 0xBE, 0x10, 0xBF, 0x10, 0xC0, 0x10, ++ 0xC1, 0x10, 0xC2, 0x10, 0xC3, 0x10, 0xC4, 0x10, 0xC5, 0x10, 0xFF, 0xFF, 0x1B, 0xD2, 0x21, 0xFF, ++ 0x22, 0xFF, 0x23, 0xFF, 0x24, 0xFF, 0x25, 0xFF, 0x26, 0xFF, 0x27, 0xFF, 0x28, 0xFF, 0x29, 0xFF, ++ 0x2A, 0xFF, 0x2B, 0xFF, 0x2C, 0xFF, 0x2D, 0xFF, 0x2E, 0xFF, 0x2F, 0xFF, 0x30, 0xFF, 0x31, 0xFF, ++ 0x32, 0xFF, 0x33, 0xFF, 0x34, 0xFF, 0x35, 0xFF, 0x36, 0xFF, 0x37, 0xFF, 0x38, 0xFF, 0x39, 0xFF, ++ 0x3A, 0xFF, 0x5B, 0xFF, 0x5C, 0xFF, 0x5D, 0xFF, 0x5E, 0xFF, 0x5F, 0xFF, 0x60, 0xFF, 0x61, 0xFF, ++ 0x62, 0xFF, 0x63, 0xFF, 0x64, 0xFF, 0x65, 0xFF, 0x66, 0xFF, 0x67, 0xFF, 0x68, 0xFF, 0x69, 0xFF, ++ 0x6A, 0xFF, 0x6B, 0xFF, 0x6C, 0xFF, 0x6D, 0xFF, 0x6E, 0xFF, 0x6F, 0xFF, 0x70, 0xFF, 0x71, 0xFF, ++ 0x72, 0xFF, 0x73, 0xFF, 0x74, 0xFF, 0x75, 0xFF, 0x76, 0xFF, 0x77, 0xFF, 0x78, 0xFF, 0x79, 0xFF, ++ 0x7A, 0xFF, 0x7B, 0xFF, 0x7C, 0xFF, 0x7D, 0xFF, 0x7E, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0x81, 0xFF, ++ 0x82, 0xFF, 0x83, 0xFF, 0x84, 0xFF, 0x85, 0xFF, 0x86, 0xFF, 0x87, 0xFF, 0x88, 0xFF, 0x89, 0xFF, ++ 0x8A, 0xFF, 0x8B, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF, 0x8E, 0xFF, 0x8F, 0xFF, 0x90, 0xFF, 0x91, 0xFF, ++ 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, 0xFF, 0x96, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF, ++ 0x9A, 0xFF, 0x9B, 0xFF, 0x9C, 0xFF, 0x9D, 0xFF, 0x9E, 0xFF, 0x9F, 0xFF, 0xA0, 0xFF, 0xA1, 0xFF, ++ 0xA2, 0xFF, 0xA3, 0xFF, 0xA4, 0xFF, 0xA5, 0xFF, 0xA6, 0xFF, 0xA7, 0xFF, 0xA8, 0xFF, 0xA9, 0xFF, ++ 0xAA, 0xFF, 0xAB, 0xFF, 0xAC, 0xFF, 0xAD, 0xFF, 0xAE, 0xFF, 0xAF, 0xFF, 0xB0, 0xFF, 0xB1, 0xFF, ++ 0xB2, 0xFF, 0xB3, 0xFF, 0xB4, 0xFF, 0xB5, 0xFF, 0xB6, 0xFF, 0xB7, 0xFF, 0xB8, 0xFF, 0xB9, 0xFF, ++ 0xBA, 0xFF, 0xBB, 0xFF, 0xBC, 0xFF, 0xBD, 0xFF, 0xBE, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, ++ 0xC2, 0xFF, 0xC3, 0xFF, 0xC4, 0xFF, 0xC5, 0xFF, 0xC6, 0xFF, 0xC7, 0xFF, 0xC8, 0xFF, 0xC9, 0xFF, ++ 0xCA, 0xFF, 0xCB, 0xFF, 0xCC, 0xFF, 0xCD, 0xFF, 0xCE, 0xFF, 0xCF, 0xFF, 0xD0, 0xFF, 0xD1, 0xFF, ++ 0xD2, 0xFF, 0xD3, 0xFF, 0xD4, 0xFF, 0xD5, 0xFF, 0xD6, 0xFF, 0xD7, 0xFF, 0xD8, 0xFF, 0xD9, 0xFF, ++ 0xDA, 0xFF, 0xDB, 0xFF, 0xDC, 0xFF, 0xDD, 0xFF, 0xDE, 0xFF, 0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF, ++ 0xE2, 0xFF, 0xE3, 0xFF, 0xE4, 0xFF, 0xE5, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF, ++ 0xEA, 0xFF, 0xEB, 0xFF, 0xEC, 0xFF, 0xED, 0xFF, 0xEE, 0xFF, 0xEF, 0xFF, 0xF0, 0xFF, 0xF1, 0xFF, ++ 0xF2, 0xFF, 0xF3, 0xFF, 0xF4, 0xFF, 0xF5, 0xFF, 0xF6, 0xFF, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xFF, ++ 0xFA, 0xFF, 0xFB, 0xFF, 0xFC, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF ++}; +diff --git a/drivers/staging/exfat/exfat_version.h b/drivers/staging/exfat/exfat_version.h +new file mode 100644 +index 00000000..ab6ce18b +--- /dev/null ++++ b/drivers/staging/exfat/exfat_version.h +@@ -0,0 +1 @@ ++#define EXFAT_VERSION "1.2.4" +-- +1.8.4.3.gca3854a + |
