fs/exfat: import oriGinal library v1.2.9
authorShiyong Li <a22381@motorola.com>
Thu, 7 Dec 2017 00:16:32 +0000 (16:16 -0800)
committerxiest1 <xiest1@lenovo.com>
Tue, 5 Nov 2019 09:30:24 +0000 (17:30 +0800)
oriGinal repo: https://github.com/dorimanx/exfat-nofuse

latest commit:
commit de4c760
Author: Yuri.Sh <dorimanx@users.noreply.github.com>
Date:   Tue Jun 20 00:56:18 2017 +0300

    Merge pull request #116 from ian-abbott/no-krel

    Makefile: remove KREL

Change-Id: I918688177815d21a3227e740ef36680f0913d4f9
Signed-off-by: Shiyong Li <a22381@motorola.com>
Reviewed-on: https://gerrit.mot.com/1102174
SLTApproved: Slta Waiver
SME-Granted: SME Approvals Granted
Tested-by: Jira Key
Reviewed-by: Igor Kovalenko <igork@motorola.com>
Submit-Approved: Jira Key
Reviewed-on: https://gerrit.mot.com/1276876
Reviewed-by: Hua Tan <tanhua1@motorola.com>
29 files changed:
fs/Kconfig
fs/Makefile
fs/exfat/Kconfig [new file with mode: 0644]
fs/exfat/LICENSE [new file with mode: 0644]
fs/exfat/Makefile [new file with mode: 0644]
fs/exfat/README.md [new file with mode: 0644]
fs/exfat/dkms.conf [new file with mode: 0644]
fs/exfat/exfat-km.mk [new file with mode: 0644]
fs/exfat/exfat_api.c [new file with mode: 0644]
fs/exfat/exfat_api.h [new file with mode: 0644]
fs/exfat/exfat_bitmap.c [new file with mode: 0644]
fs/exfat/exfat_bitmap.h [new file with mode: 0644]
fs/exfat/exfat_blkdev.c [new file with mode: 0644]
fs/exfat/exfat_blkdev.h [new file with mode: 0644]
fs/exfat/exfat_cache.c [new file with mode: 0644]
fs/exfat/exfat_cache.h [new file with mode: 0644]
fs/exfat/exfat_config.h [new file with mode: 0644]
fs/exfat/exfat_core.c [new file with mode: 0644]
fs/exfat/exfat_core.h [new file with mode: 0644]
fs/exfat/exfat_data.c [new file with mode: 0644]
fs/exfat/exfat_data.h [new file with mode: 0644]
fs/exfat/exfat_nls.c [new file with mode: 0644]
fs/exfat/exfat_nls.h [new file with mode: 0644]
fs/exfat/exfat_oal.c [new file with mode: 0644]
fs/exfat/exfat_oal.h [new file with mode: 0644]
fs/exfat/exfat_super.c [new file with mode: 0644]
fs/exfat/exfat_super.h [new file with mode: 0644]
fs/exfat/exfat_upcase.c [new file with mode: 0644]
fs/exfat/exfat_version.h [new file with mode: 0644]

index 121fabf122f2834e1742686042b966d0986af3d1..0b8fce02029797b7c1ec20d2f6e70f209054492b 100644 (file)
@@ -125,6 +125,7 @@ if BLOCK
 menu "DOS/FAT/NT Filesystems"
 
 source "fs/fat/Kconfig"
+source "fs/exfat/Kconfig"
 source "fs/ntfs/Kconfig"
 
 endmenu
index 1e34e4bc81a33d3c9eb818ee28c6012d79d95e48..cc438741a13d8ed848ad8a0794de4b295c65c223 100644 (file)
@@ -79,6 +79,7 @@ obj-$(CONFIG_HUGETLBFS)               += hugetlbfs/
 obj-$(CONFIG_CODA_FS)          += coda/
 obj-$(CONFIG_MINIX_FS)         += minix/
 obj-$(CONFIG_FAT_FS)           += fat/
+obj-$(CONFIG_EXFAT_FS)         += exfat/
 obj-$(CONFIG_BFS_FS)           += bfs/
 obj-$(CONFIG_ISO9660_FS)       += isofs/
 obj-$(CONFIG_HFSPLUS_FS)       += hfsplus/ # Before hfs to find wrapped HFS+
diff --git a/fs/exfat/Kconfig b/fs/exfat/Kconfig
new file mode 100644 (file)
index 0000000..78b32aa
--- /dev/null
@@ -0,0 +1,39 @@
+config EXFAT_FS
+       tristate "exFAT fs support"
+       select NLS
+       help
+         This adds support for the exFAT file system.
+
+config EXFAT_DISCARD
+       bool "enable discard support"
+       depends on EXFAT_FS
+       default y
+
+config EXFAT_DELAYED_SYNC
+       bool "enable delayed sync"
+       depends on EXFAT_FS
+       default n
+
+config EXFAT_KERNEL_DEBUG
+       bool "enable kernel debug features via ioctl"
+       depends on EXFAT_FS
+       default n
+
+config EXFAT_DEBUG_MSG
+       bool "print debug messages"
+       depends on EXFAT_FS
+       default n
+
+config EXFAT_DEFAULT_CODEPAGE
+       int "Default codepage for exFAT"
+       default 437
+       depends on EXFAT_FS
+       help
+         This option should be set to the codepage of your exFAT filesystems.
+
+config EXFAT_DEFAULT_IOCHARSET
+       string "Default iocharset for exFAT"
+       default "utf8"
+       depends on EXFAT_FS
+       help
+         Set this to the default input/output character set you'd like exFAT to use.
diff --git a/fs/exfat/LICENSE b/fs/exfat/LICENSE
new file mode 100644 (file)
index 0000000..d159169
--- /dev/null
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/fs/exfat/Makefile b/fs/exfat/Makefile
new file mode 100644 (file)
index 0000000..711ed87
--- /dev/null
@@ -0,0 +1,54 @@
+#
+# Makefile for Linux FAT12/FAT16/FAT32(VFAT)/FAT64(ExFAT) filesystem driver.
+#
+
+ifneq ($(KERNELRELEASE),)
+# call from kernel build system
+
+obj-$(CONFIG_EXFAT_FS) += exfat.o
+
+exfat-objs := exfat_core.o exfat_super.o exfat_api.o exfat_blkdev.o exfat_cache.o \
+                          exfat_data.o exfat_bitmap.o exfat_nls.o exfat_oal.o exfat_upcase.o
+
+else
+# external module build
+
+EXTRA_FLAGS += -I$(PWD)
+
+#
+# KDIR is a path to a directory containing kernel source.
+# It can be specified on the command line passed to make to enable the module to
+# be built and installed for a kernel other than the one currently running.
+# By default it is the path to the symbolic link created when
+# the current kernel's modules were installed, but
+# any valid path to the directory in which the target kernel's source is located
+# can be provided on the command line.
+#
+KDIR   ?= /lib/modules/$(shell uname -r)/build
+MDIR   ?= /lib/modules/$(shell uname -r)
+PWD    := $(shell pwd)
+PWD    := $(shell pwd)
+
+export CONFIG_EXFAT_FS := m
+
+all:
+       $(MAKE) -C $(KDIR) M=$(PWD) modules
+
+clean:
+       $(MAKE) -C $(KDIR) M=$(PWD) clean
+
+help:
+       $(MAKE) -C $(KDIR) M=$(PWD) help
+
+install: exfat.ko
+       rm -f ${MDIR}/kernel/fs/exfat/exfat.ko
+       install -m644 -b -D exfat.ko ${MDIR}/kernel/fs/exfat/exfat.ko
+       depmod -aq
+
+uninstall:
+       rm -rf ${MDIR}/kernel/fs/exfat
+       depmod -aq
+
+endif
+
+.PHONY : all clean install uninstall
diff --git a/fs/exfat/README.md b/fs/exfat/README.md
new file mode 100644 (file)
index 0000000..feab400
--- /dev/null
@@ -0,0 +1,98 @@
+exfat-nofuse
+============
+
+Linux non-fuse read/write kernel driver for the exFAT, FAT12, FAT16 and vfat (FAT32) file systems.<br />
+Originally ported from Android kernel v3.0.
+
+Kudos to ksv1986 for the mutex patch!<br />
+Thanks to JackNorris for being awesome and providing the clear_inode() patch.<br />
+<br />
+Big thanks to lqs for completing the driver!<br />
+Big thanks to benpicco for fixing 3.11.y compatibility!
+
+
+Special thanks to github user AndreiLux for spreading the word about the leak!<br />
+
+
+Installing as a stand-alone module:
+====================================
+
+    make
+    sudo make install
+
+To load the driver manually, run this as root:
+
+    modprobe exfat
+
+You may also specify custom toolchains by using CROSS_COMPILE flag, in my case:
+>CROSS_COMPILE=../dorimanx-SG2-I9100-Kernel/android-toolchain/bin/arm-eabi-
+
+Installing as a part of the kernel:
+======================================
+
+Let's take [linux] as the path to your kernel source dir...
+
+       cd [linux]
+       cp -rvf exfat-nofuse [linux]/fs/exfat
+
+edit [linux]/fs/Kconfig
+```
+ menu "DOS/FAT/NT Filesystems"
+
+  source "fs/fat/Kconfig"
+ +source "fs/exfat/Kconfig"
+  source "fs/ntfs/Kconfig"
+  endmenu
+```
+  
+
+edit [linux]/fs/Makefile
+```
+  obj-$(CONFIG_FAT_FS)    += fat/
+ +obj-$(CONFIG_EXFAT_FS)  += exfat/
+  obj-$(CONFIG_BFS_FS)    += bfs/
+```
+
+       cd [linux]
+       make menuconfig
+
+Go to:
+> File systems > DOS/FAT/NT
+>   check exfat as MODULE (M)
+>   (437) Default codepage for exFAT
+>   (utf8) Default iocharset for exFAT
+
+> ESC to main menu
+> Save an Alternate Configuration File
+> ESC ESC
+
+build your kernel
+
+Have fun.
+
+
+Installing as a DKMS module:
+=================================
+
+You can have even more fun with exfat-nofuse by installing it as a DKMS module has the main advantage of being auto-compiled (and thus, possibly surviving) between kernel upgrades.
+
+First, get dkms. On Ubuntu this should be:
+
+       sudo apt install dkms
+
+Then copy the root of this repository to /usr/share:
+
+       sudo cp -R . /usr/src/exfat-1.2.8 (or whatever version number declared on dkms.conf is)
+       sudo dkms add -m exfat -v 1.2.8
+
+Build and load the module:
+
+       sudo dkms build -m exfat -v 1.2.8
+       sudo dkms install -m exfat -v 1.2.8
+
+Now you have a proper dkms module that will work for a long time... hopefully.
+
+
+
+Free Software for the Free Minds!
+=================================
diff --git a/fs/exfat/dkms.conf b/fs/exfat/dkms.conf
new file mode 100644 (file)
index 0000000..d873c0a
--- /dev/null
@@ -0,0 +1,7 @@
+PACKAGE_NAME="exfat"
+PACKAGE_VERSION="1.2.8"
+MAKE="KDIR=/lib/modules/$kernelver/build MDIR=/lib/modules/$kernelver make"
+CLEAN="make clean"
+BUILT_MODULE_NAME[0]="exfat"
+AUTOINSTALL="yes"
+DEST_MODULE_LOCATION="/extra"
diff --git a/fs/exfat/exfat-km.mk b/fs/exfat/exfat-km.mk
new file mode 100644 (file)
index 0000000..4e3ef07
--- /dev/null
@@ -0,0 +1,11 @@
+EXFAT_FOLDER ?= external/exfat-nofuse
+
+EXFAT_MODULE:
+       make clean -C $(EXFAT_FOLDER) KDIR=$(KERNEL_OUT)
+       make -j8 -C $(EXFAT_FOLDER) ARCH=arm KDIR=$(KERNEL_OUT) \
+               $(if $(ARM_CROSS_COMPILE),$(ARM_CROSS_COMPILE),$(KERNEL_CROSS_COMPILE))
+       mv $(EXFAT_FOLDER)/exfat.ko $(KERNEL_MODULES_OUT)
+       $(if $(ARM_EABI_TOOLCHAIN),$(ARM_EABI_TOOLCHAIN)/arm-eabi-strip, \
+               $(KERNEL_TOOLCHAIN_PATH)strip) --strip-unneeded $(KERNEL_MODULES_OUT)/exfat.ko
+
+TARGET_KERNEL_MODULES += EXFAT_MODULE
diff --git a/fs/exfat/exfat_api.c b/fs/exfat/exfat_api.c
new file mode 100644 (file)
index 0000000..32b29f0
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_api.c                                               */
+/*  PURPOSE : exFAT API Glue Layer                                      */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "exfat_version.h"
+#include "exfat_config.h"
+#include "exfat_data.h"
+#include "exfat_oal.h"
+
+#include "exfat_nls.h"
+#include "exfat_api.h"
+#include "exfat_super.h"
+#include "exfat_core.h"
+
+/*----------------------------------------------------------------------*/
+/*  Constant & Macro Definitions                                        */
+/*----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+/*  Global Variable Definitions                                         */
+/*----------------------------------------------------------------------*/
+
+extern struct semaphore z_sem;
+
+/*----------------------------------------------------------------------*/
+/*  Local Variable Definitions                                          */
+/*----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+/*  Local Function Declarations                                         */
+/*----------------------------------------------------------------------*/
+
+/*======================================================================*/
+/*  Global Function Definitions                                         */
+/*    - All functions for global use have same return value format,     */
+/*      that is, FFS_SUCCESS on success and several FS error code on    */
+/*      various error condition.                                        */
+/*======================================================================*/
+
+/*----------------------------------------------------------------------*/
+/*  exFAT Filesystem Init & Exit Functions                              */
+/*----------------------------------------------------------------------*/
+
+int FsInit(void)
+{
+       return ffsInit();
+}
+
+int FsShutdown(void)
+{
+       return ffsShutdown();
+}
+
+/*----------------------------------------------------------------------*/
+/*  Volume Management Functions                                         */
+/*----------------------------------------------------------------------*/
+
+/* FsMountVol : mount the file system volume */
+int FsMountVol(struct super_block *sb)
+{
+       int err;
+
+       sm_P(&z_sem);
+
+       err = buf_init(sb);
+       if (!err)
+               err = ffsMountVol(sb);
+       else
+               buf_shutdown(sb);
+
+       sm_V(&z_sem);
+
+       return err;
+} /* end of FsMountVol */
+
+/* FsUmountVol : unmount the file system volume */
+int FsUmountVol(struct super_block *sb)
+{
+       int err;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       sm_P(&z_sem);
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsUmountVol(sb);
+       buf_shutdown(sb);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       sm_V(&z_sem);
+
+       return err;
+} /* end of FsUmountVol */
+
+/* FsGetVolInfo : get the information of a file system volume */
+int FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info)
+{
+       int err;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* check the validity of pointer parameters */
+       if (info == NULL)
+               return FFS_ERROR;
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsGetVolInfo(sb, info);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsGetVolInfo */
+
+/* FsSyncVol : synchronize a file system volume */
+int FsSyncVol(struct super_block *sb, int do_sync)
+{
+       int err;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsSyncVol(sb, do_sync);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsSyncVol */
+
+
+/*----------------------------------------------------------------------*/
+/*  File Operation Functions                                            */
+/*----------------------------------------------------------------------*/
+
+/* FsCreateFile : create a file */
+int FsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid)
+{
+       int err;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* check the validity of pointer parameters */
+       if ((fid == NULL) || (path == NULL) || (*path == '\0'))
+               return FFS_ERROR;
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsLookupFile(inode, path, fid);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsLookupFile */
+
+/* FsCreateFile : create a file */
+int FsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid)
+{
+       int err;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* check the validity of pointer parameters */
+       if ((fid == NULL) || (path == NULL) || (*path == '\0'))
+               return FFS_ERROR;
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsCreateFile(inode, path, mode, fid);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsCreateFile */
+
+int FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount)
+{
+       int err;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* check the validity of the given file id */
+       if (fid == NULL)
+               return FFS_INVALIDFID;
+
+       /* check the validity of pointer parameters */
+       if (buffer == NULL)
+               return FFS_ERROR;
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsReadFile(inode, fid, buffer, count, rcount);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsReadFile */
+
+int FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount)
+{
+       int err;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* check the validity of the given file id */
+       if (fid == NULL)
+               return FFS_INVALIDFID;
+
+       /* check the validity of pointer parameters */
+       if (buffer == NULL)
+               return FFS_ERROR;
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsWriteFile(inode, fid, buffer, count, wcount);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsWriteFile */
+
+/* FsTruncateFile : resize the file length */
+int FsTruncateFile(struct inode *inode, u64 old_size, u64 new_size)
+{
+       int err;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       DPRINTK("FsTruncateFile entered (inode %p size %llu)\n", inode, new_size);
+
+       err = ffsTruncateFile(inode, old_size, new_size);
+
+       DPRINTK("FsTruncateFile exitted (%d)\n", err);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsTruncateFile */
+
+/* FsMoveFile : move(rename) a old file into a new file */
+int FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry)
+{
+       int err;
+       struct super_block *sb = old_parent_inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* check the validity of the given file id */
+       if (fid == NULL)
+               return FFS_INVALIDFID;
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsMoveFile(old_parent_inode, fid, new_parent_inode, new_dentry);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsMoveFile */
+
+/* FsRemoveFile : remove a file */
+int FsRemoveFile(struct inode *inode, FILE_ID_T *fid)
+{
+       int err;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* check the validity of the given file id */
+       if (fid == NULL)
+               return FFS_INVALIDFID;
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsRemoveFile(inode, fid);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsRemoveFile */
+
+/* FsSetAttr : set the attribute of a given file */
+int FsSetAttr(struct inode *inode, u32 attr)
+{
+       int err;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsSetAttr(inode, attr);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsSetAttr */
+
+/* FsReadStat : get the information of a given file */
+int FsReadStat(struct inode *inode, DIR_ENTRY_T *info)
+{
+       int err;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsGetStat(inode, info);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsReadStat */
+
+/* FsWriteStat : set the information of a given file */
+int FsWriteStat(struct inode *inode, DIR_ENTRY_T *info)
+{
+       int err;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       DPRINTK("FsWriteStat entered (inode %p info %p\n", inode, info);
+
+       err = ffsSetStat(inode, info);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       DPRINTK("FsWriteStat exited (%d)\n", err);
+
+       return err;
+} /* end of FsWriteStat */
+
+/* FsMapCluster : return the cluster number in the given cluster offset */
+int FsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu)
+{
+       int err;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* check the validity of pointer parameters */
+       if (clu == NULL)
+               return FFS_ERROR;
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsMapCluster(inode, clu_offset, clu);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsMapCluster */
+
+/*----------------------------------------------------------------------*/
+/*  Directory Operation Functions                                       */
+/*----------------------------------------------------------------------*/
+
+/* FsCreateDir : create(make) a directory */
+int FsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid)
+{
+       int err;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* check the validity of pointer parameters */
+       if ((fid == NULL) || (path == NULL) || (*path == '\0'))
+               return FFS_ERROR;
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsCreateDir(inode, path, fid);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsCreateDir */
+
+/* FsReadDir : read a directory entry from the opened directory */
+int FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry)
+{
+       int err;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* check the validity of pointer parameters */
+       if (dir_entry == NULL)
+               return FFS_ERROR;
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsReadDir(inode, dir_entry);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsReadDir */
+
+/* FsRemoveDir : remove a directory */
+int FsRemoveDir(struct inode *inode, FILE_ID_T *fid)
+{
+       int err;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* check the validity of the given file id */
+       if (fid == NULL)
+               return FFS_INVALIDFID;
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       err = ffsRemoveDir(inode, fid);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return err;
+} /* end of FsRemoveDir */
+
+EXPORT_SYMBOL(FsMountVol);
+EXPORT_SYMBOL(FsUmountVol);
+EXPORT_SYMBOL(FsGetVolInfo);
+EXPORT_SYMBOL(FsSyncVol);
+EXPORT_SYMBOL(FsLookupFile);
+EXPORT_SYMBOL(FsCreateFile);
+EXPORT_SYMBOL(FsReadFile);
+EXPORT_SYMBOL(FsWriteFile);
+EXPORT_SYMBOL(FsTruncateFile);
+EXPORT_SYMBOL(FsMoveFile);
+EXPORT_SYMBOL(FsRemoveFile);
+EXPORT_SYMBOL(FsSetAttr);
+EXPORT_SYMBOL(FsReadStat);
+EXPORT_SYMBOL(FsWriteStat);
+EXPORT_SYMBOL(FsMapCluster);
+EXPORT_SYMBOL(FsCreateDir);
+EXPORT_SYMBOL(FsReadDir);
+EXPORT_SYMBOL(FsRemoveDir);
+
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+/* FsReleaseCache: Release FAT & buf cache */
+int FsReleaseCache(struct super_block *sb)
+{
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* acquire the lock for file system critical section */
+       sm_P(&p_fs->v_sem);
+
+       FAT_release_all(sb);
+       buf_release_all(sb);
+
+       /* release the lock for file system critical section */
+       sm_V(&p_fs->v_sem);
+
+       return 0;
+}
+/* FsReleaseCache */
+
+EXPORT_SYMBOL(FsReleaseCache);
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+
+/*======================================================================*/
+/*  Local Function Definitions                                          */
+/*======================================================================*/
diff --git a/fs/exfat/exfat_api.h b/fs/exfat/exfat_api.h
new file mode 100644 (file)
index 0000000..84bdf61
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_api.h                                               */
+/*  PURPOSE : Header File for exFAT API Glue Layer                      */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#ifndef _EXFAT_API_H
+#define _EXFAT_API_H
+
+#include <linux/fs.h>
+#include "exfat_config.h"
+
+/*----------------------------------------------------------------------*/
+/*  Constant & Macro Definitions                                        */
+/*----------------------------------------------------------------------*/
+
+#define EXFAT_SUPER_MAGIC       (0x2011BAB0L)
+#define EXFAT_ROOT_INO          1
+
+/* FAT types */
+#define FAT12                   0x01    /* FAT12 */
+#define FAT16                   0x0E    /* Win95 FAT16 (LBA) */
+#define FAT32                   0x0C    /* Win95 FAT32 (LBA) */
+#define EXFAT                   0x07    /* exFAT */
+
+/* file name lengths */
+#define MAX_CHARSET_SIZE        3       /* max size of multi-byte character    */
+#define MAX_PATH_DEPTH          15      /* max depth of path name */
+#define MAX_NAME_LENGTH         256     /* max len of file name including NULL */
+#define MAX_PATH_LENGTH         260     /* max len of path name including NULL */
+#define DOS_NAME_LENGTH         11      /* DOS file name length excluding NULL */
+#define DOS_PATH_LENGTH         80      /* DOS path name length excluding NULL */
+
+/* file attributes */
+#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
+
+/* file creation modes */
+#define FM_REGULAR              0x00
+#define FM_SYMLINK              0x40
+
+/* return values */
+#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
+
+/*----------------------------------------------------------------------*/
+/*  Type Definitions                                                    */
+/*----------------------------------------------------------------------*/
+
+typedef struct {
+       u16      Year;
+       u16      Month;
+       u16      Day;
+       u16      Hour;
+       u16      Minute;
+       u16      Second;
+       u16      MilliSecond;
+} DATE_TIME_T;
+
+typedef struct {
+       u32      Offset;    /* start sector number of the partition */
+       u32      Size;      /* in sectors */
+} PART_INFO_T;
+
+typedef struct {
+       u32      SecSize;    /* sector size in bytes */
+       u32      DevSize;    /* block device size in sectors */
+} DEV_INFO_T;
+
+typedef struct {
+       u32      FatType;
+       u32      ClusterSize;
+       u32      NumClusters;
+       u32      FreeClusters;
+       u32      UsedClusters;
+} VOL_INFO_T;
+
+/* directory structure */
+typedef struct {
+       u32      dir;
+       s32       size;
+       u8       flags;
+} CHAIN_T;
+
+/* file id structure */
+typedef struct {
+       CHAIN_T     dir;
+       s32       entry;
+       u32      type;
+       u32      attr;
+       u32      start_clu;
+       u64      size;
+       u8       flags;
+       s64       rwoffset;
+       s32       hint_last_off;
+       u32      hint_last_clu;
+} FILE_ID_T;
+
+typedef struct {
+       char        Name[MAX_NAME_LENGTH * MAX_CHARSET_SIZE];
+       char        ShortName[DOS_NAME_LENGTH + 2];     /* used only for FAT12/16/32, not used for exFAT */
+       u32      Attr;
+       u64      Size;
+       u32      NumSubdirs;
+       DATE_TIME_T CreateTimestamp;
+       DATE_TIME_T ModifyTimestamp;
+       DATE_TIME_T AccessTimestamp;
+} DIR_ENTRY_T;
+
+/*======================================================================*/
+/*                                                                      */
+/*                     API FUNCTION DECLARATIONS                        */
+/*                  (CHANGE THIS PART IF REQUIRED)                      */
+/*                                                                      */
+/*======================================================================*/
+
+/*----------------------------------------------------------------------*/
+/*  External Function Declarations                                      */
+/*----------------------------------------------------------------------*/
+
+/* file system initialization & shutdown functions */
+       int FsInit(void);
+       int FsShutdown(void);
+
+/* volume management functions */
+       int FsMountVol(struct super_block *sb);
+       int FsUmountVol(struct super_block *sb);
+       int FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info);
+       int FsSyncVol(struct super_block *sb, int do_sync);
+
+/* file management functions */
+       int FsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid);
+       int FsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid);
+       int FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount);
+       int FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount);
+       int FsTruncateFile(struct inode *inode, u64 old_size, u64 new_size);
+       int FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry);
+       int FsRemoveFile(struct inode *inode, FILE_ID_T *fid);
+       int FsSetAttr(struct inode *inode, u32 attr);
+       int FsReadStat(struct inode *inode, DIR_ENTRY_T *info);
+       int FsWriteStat(struct inode *inode, DIR_ENTRY_T *info);
+       int FsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu);
+
+/* directory management functions */
+       int FsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid);
+       int FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry);
+       int FsRemoveDir(struct inode *inode, FILE_ID_T *fid);
+
+/* debug functions */
+s32 FsReleaseCache(struct super_block *sb);
+
+#endif /* _EXFAT_API_H */
diff --git a/fs/exfat/exfat_bitmap.c b/fs/exfat/exfat_bitmap.c
new file mode 100644 (file)
index 0000000..b0672dd
--- /dev/null
@@ -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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_global.c                                            */
+/*  PURPOSE : exFAT Miscellaneous Functions                             */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#include "exfat_config.h"
+#include "exfat_bitmap.h"
+
+/*----------------------------------------------------------------------*/
+/*  Bitmap Manipulation Functions                                       */
+/*----------------------------------------------------------------------*/
+
+#define BITMAP_LOC(v)           ((v) >> 3)
+#define BITMAP_SHIFT(v)         ((v) & 0x07)
+
+s32 exfat_bitmap_test(u8 *bitmap, int i)
+{
+       u8 data;
+
+       data = bitmap[BITMAP_LOC(i)];
+       if ((data >> BITMAP_SHIFT(i)) & 0x01)
+               return 1;
+       return 0;
+} /* end of Bitmap_test */
+
+void exfat_bitmap_set(u8 *bitmap, int i)
+{
+       bitmap[BITMAP_LOC(i)] |= (0x01 << BITMAP_SHIFT(i));
+} /* end of Bitmap_set */
+
+void exfat_bitmap_clear(u8 *bitmap, int i)
+{
+       bitmap[BITMAP_LOC(i)] &= ~(0x01 << BITMAP_SHIFT(i));
+} /* end of Bitmap_clear */
diff --git a/fs/exfat/exfat_bitmap.h b/fs/exfat/exfat_bitmap.h
new file mode 100644 (file)
index 0000000..4f482c7
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_global.h                                            */
+/*  PURPOSE : Header File for exFAT Global Definitions & Misc Functions */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#ifndef _EXFAT_BITMAP_H
+#define _EXFAT_BITMAP_H
+
+#include <linux/types.h>
+
+/*======================================================================*/
+/*                                                                      */
+/*       LIBRARY FUNCTION DECLARATIONS -- OTHER UTILITY FUNCTIONS       */
+/*                    (DO NOT CHANGE THIS PART !!)                      */
+/*                                                                      */
+/*======================================================================*/
+
+/*----------------------------------------------------------------------*/
+/*  Bitmap Manipulation Functions                                       */
+/*----------------------------------------------------------------------*/
+
+s32    exfat_bitmap_test(u8 *bitmap, int i);
+void   exfat_bitmap_set(u8 *bitmap, int i);
+void   exfat_bitmap_clear(u8 *bitmpa, int i);
+
+#endif /* _EXFAT_BITMAP_H */
diff --git a/fs/exfat/exfat_blkdev.c b/fs/exfat/exfat_blkdev.c
new file mode 100644 (file)
index 0000000..eaccfd8
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_blkdev.c                                            */
+/*  PURPOSE : exFAT Block Device Driver Glue Layer                      */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#include <linux/blkdev.h>
+#include <linux/log2.h>
+#include "exfat_config.h"
+#include "exfat_blkdev.h"
+#include "exfat_data.h"
+#include "exfat_api.h"
+#include "exfat_super.h"
+
+/*----------------------------------------------------------------------*/
+/*  Constant & Macro Definitions                                        */
+/*----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+/*  Global Variable Definitions                                         */
+/*----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+/*  Local Variable Definitions                                          */
+/*----------------------------------------------------------------------*/
+
+/*======================================================================*/
+/*  Function Definitions                                                */
+/*======================================================================*/
+
+s32 bdev_init(void)
+{
+       return FFS_SUCCESS;
+}
+
+s32 bdev_shutdown(void)
+{
+       return FFS_SUCCESS;
+}
+
+s32 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 = ilog2(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;
+}
+
+s32 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;
+}
+
+s32 bdev_read(struct super_block *sb, sector_t secno, struct buffer_head **bh, u32 num_secs, s32 read)
+{
+       BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+       struct exfat_sb_info *sbi = EXFAT_SB(sb);
+       long flags = sbi->debug_flags;
+
+       if (flags & EXFAT_DEBUGFLAGS_ERROR_RW)
+               return FFS_MEDIAERR;
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+
+       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;
+}
+
+s32 bdev_write(struct super_block *sb, sector_t secno, struct buffer_head *bh, u32 num_secs, s32 sync)
+{
+       s32 count;
+       struct buffer_head *bh2;
+       BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+       struct exfat_sb_info *sbi = EXFAT_SB(sb);
+       long flags = sbi->debug_flags;
+
+       if (flags & EXFAT_DEBUGFLAGS_ERROR_RW)
+               return FFS_MEDIAERR;
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+
+       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;
+}
+
+s32 bdev_sync(struct super_block *sb)
+{
+       BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+       struct exfat_sb_info *sbi = EXFAT_SB(sb);
+       long flags = sbi->debug_flags;
+
+       if (flags & EXFAT_DEBUGFLAGS_ERROR_RW)
+               return FFS_MEDIAERR;
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+
+       if (!p_bd->opened)
+               return FFS_MEDIAERR;
+
+       return sync_blockdev(sb->s_bdev);
+}
diff --git a/fs/exfat/exfat_blkdev.h b/fs/exfat/exfat_blkdev.h
new file mode 100644 (file)
index 0000000..3363b59
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_blkdev.h                                            */
+/*  PURPOSE : Header File for exFAT Block Device Driver Glue Layer      */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#ifndef _EXFAT_BLKDEV_H
+#define _EXFAT_BLKDEV_H
+
+#include <linux/fs.h>
+#include "exfat_config.h"
+
+/*----------------------------------------------------------------------*/
+/*  Constant & Macro Definitions (Non-Configurable)                     */
+/*----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+/*  Type Definitions                                                    */
+/*----------------------------------------------------------------------*/
+
+typedef struct __BD_INFO_T {
+       s32 sector_size;      /* in bytes */
+       s32 sector_size_bits;
+       s32 sector_size_mask;
+       s32 num_sectors;      /* total number of sectors in this block device */
+       bool  opened;           /* opened or not */
+} BD_INFO_T;
+
+/*----------------------------------------------------------------------*/
+/*  External Variable Declarations                                      */
+/*----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+/*  External Function Declarations                                      */
+/*----------------------------------------------------------------------*/
+
+s32 bdev_init(void);
+s32 bdev_shutdown(void);
+s32 bdev_open(struct super_block *sb);
+s32 bdev_close(struct super_block *sb);
+s32 bdev_read(struct super_block *sb, sector_t secno, struct buffer_head **bh, u32 num_secs, s32 read);
+s32 bdev_write(struct super_block *sb, sector_t secno, struct buffer_head *bh, u32 num_secs, s32 sync);
+s32 bdev_sync(struct super_block *sb);
+
+#endif /* _EXFAT_BLKDEV_H */
diff --git a/fs/exfat/exfat_cache.c b/fs/exfat/exfat_cache.c
new file mode 100644 (file)
index 0000000..4130102
--- /dev/null
@@ -0,0 +1,784 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_cache.c                                             */
+/*  PURPOSE : exFAT Cache Manager                                       */
+/*            (FAT Cache & Buffer Cache)                                */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Sung-Kwan Kim] : first writing                        */
+/*                                                                      */
+/************************************************************************/
+
+#include "exfat_config.h"
+#include "exfat_data.h"
+
+#include "exfat_cache.h"
+#include "exfat_super.h"
+#include "exfat_core.h"
+
+/*----------------------------------------------------------------------*/
+/*  Global Variable Definitions                                         */
+/*----------------------------------------------------------------------*/
+
+#define sm_P(s)
+#define sm_V(s)
+
+static s32 __FAT_read(struct super_block *sb, u32 loc, u32 *content);
+static s32 __FAT_write(struct super_block *sb, u32 loc, u32 content);
+
+static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, sector_t sec);
+static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, sector_t 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 u8 *__buf_getblk(struct super_block *sb, sector_t sec);
+
+static BUF_CACHE_T *buf_cache_find(struct super_block *sb, sector_t sec);
+static BUF_CACHE_T *buf_cache_get(struct super_block *sb, sector_t 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);
+
+/*======================================================================*/
+/*  Cache Initialization Functions                                      */
+/*======================================================================*/
+
+s32 buf_init(struct super_block *sb)
+{
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       int i;
+
+       /* LRU list */
+       p_fs->FAT_cache_lru_list.next = p_fs->FAT_cache_lru_list.prev = &p_fs->FAT_cache_lru_list;
+
+       for (i = 0; i < FAT_CACHE_SIZE; i++) {
+               p_fs->FAT_cache_array[i].drv = -1;
+               p_fs->FAT_cache_array[i].sec = ~0;
+               p_fs->FAT_cache_array[i].flag = 0;
+               p_fs->FAT_cache_array[i].buf_bh = NULL;
+               p_fs->FAT_cache_array[i].prev = p_fs->FAT_cache_array[i].next = NULL;
+               push_to_mru(&(p_fs->FAT_cache_array[i]), &p_fs->FAT_cache_lru_list);
+       }
+
+       p_fs->buf_cache_lru_list.next = p_fs->buf_cache_lru_list.prev = &p_fs->buf_cache_lru_list;
+
+       for (i = 0; i < BUF_CACHE_SIZE; i++) {
+               p_fs->buf_cache_array[i].drv = -1;
+               p_fs->buf_cache_array[i].sec = ~0;
+               p_fs->buf_cache_array[i].flag = 0;
+               p_fs->buf_cache_array[i].buf_bh = NULL;
+               p_fs->buf_cache_array[i].prev = p_fs->buf_cache_array[i].next = NULL;
+               push_to_mru(&(p_fs->buf_cache_array[i]), &p_fs->buf_cache_lru_list);
+       }
+
+       /* HASH list */
+       for (i = 0; i < FAT_CACHE_HASH_SIZE; i++) {
+               p_fs->FAT_cache_hash_list[i].drv = -1;
+               p_fs->FAT_cache_hash_list[i].sec = ~0;
+               p_fs->FAT_cache_hash_list[i].hash_next = p_fs->FAT_cache_hash_list[i].hash_prev = &(p_fs->FAT_cache_hash_list[i]);
+       }
+
+       for (i = 0; i < FAT_CACHE_SIZE; i++)
+               FAT_cache_insert_hash(sb, &(p_fs->FAT_cache_array[i]));
+
+       for (i = 0; i < BUF_CACHE_HASH_SIZE; i++) {
+               p_fs->buf_cache_hash_list[i].drv = -1;
+               p_fs->buf_cache_hash_list[i].sec = ~0;
+               p_fs->buf_cache_hash_list[i].hash_next = p_fs->buf_cache_hash_list[i].hash_prev = &(p_fs->buf_cache_hash_list[i]);
+       }
+
+       for (i = 0; i < BUF_CACHE_SIZE; i++)
+               buf_cache_insert_hash(sb, &(p_fs->buf_cache_array[i]));
+
+       return FFS_SUCCESS;
+} /* end of buf_init */
+
+s32 buf_shutdown(struct super_block *sb)
+{
+       return FFS_SUCCESS;
+} /* end of buf_shutdown */
+
+/*======================================================================*/
+/*  FAT Read/Write Functions                                            */
+/*======================================================================*/
+
+/* in : sb, loc
+  * out: content
+  * returns 0 on success
+  *            -1 on error
+  */
+s32 FAT_read(struct super_block *sb, u32 loc, u32 *content)
+{
+       s32 ret;
+
+       sm_P(&f_sem);
+
+       ret = __FAT_read(sb, loc, content);
+
+       sm_V(&f_sem);
+
+       return ret;
+} /* end of FAT_read */
+
+s32 FAT_write(struct super_block *sb, u32 loc, u32 content)
+{
+       s32 ret;
+
+       sm_P(&f_sem);
+
+       ret = __FAT_write(sb, loc, content);
+
+       sm_V(&f_sem);
+
+       return ret;
+} /* end of FAT_write */
+
+static s32 __FAT_read(struct super_block *sb, u32 loc, u32 *content)
+{
+       s32 off;
+       u32 _content;
+       sector_t sec;
+       u8 *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  = (u32) fat_sector[off];
+
+                       fat_sector = FAT_getblk(sb, ++sec);
+                       if (!fat_sector)
+                               return -1;
+
+                       _content |= (u32) 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;
+} /* end of __FAT_read */
+
+static s32 __FAT_write(struct super_block *sb, u32 loc, u32 content)
+{
+       s32 off;
+       sector_t sec;
+       u8 *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) { /* odd */
+
+                       content <<= 4;
+
+                       if (off == (p_bd->sector_size-1)) {
+                               fat_sector[off] = (u8)(content | (fat_sector[off] & 0x0F));
+                               FAT_modify(sb, sec);
+
+                               fat_sector = FAT_getblk(sb, ++sec);
+                               if (!fat_sector)
+                                       return -1;
+
+                               fat_sector[0] = (u8)(content >> 8);
+                       } else {
+                               fat_entry = &(fat_sector[off]);
+                               content |= GET16(fat_entry) & 0x000F;
+
+                               SET16(fat_entry, content);
+                       }
+               } else { /* even */
+                       fat_sector[off] = (u8)(content);
+
+                       if (off == (p_bd->sector_size-1)) {
+                               fat_sector[off] = (u8)(content);
+                               FAT_modify(sb, sec);
+
+                               fat_sector = FAT_getblk(sb, ++sec);
+                               fat_sector[0] = (u8)((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 { /* p_fs->vol_type == EXFAT */
+
+               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;
+} /* end of __FAT_write */
+
+u8 *FAT_getblk(struct super_block *sb, sector_t 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;
+} /* end of FAT_getblk */
+
+void FAT_modify(struct super_block *sb, sector_t sec)
+{
+       BUF_CACHE_T *bp;
+
+       bp = FAT_cache_find(sb, sec);
+       if (bp != NULL)
+               sector_write(sb, sec, bp->buf_bh, 0);
+} /* end of FAT_modify */
+
+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);
+} /* end of FAT_release_all */
+
+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);
+} /* end of FAT_sync */
+
+static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, sector_t sec)
+{
+       s32 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;
+} /* end of FAT_cache_find */
+
+static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, sector_t 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;
+} /* end of FAT_cache_get */
+
+static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp)
+{
+       s32 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;
+} /* end of FAT_cache_insert_hash */
+
+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;
+} /* end of FAT_cache_remove_hash */
+
+/*======================================================================*/
+/*  Buffer Read/Write Functions                                         */
+/*======================================================================*/
+
+u8 *buf_getblk(struct super_block *sb, sector_t sec)
+{
+       u8 *buf;
+
+       sm_P(&b_sem);
+
+       buf = __buf_getblk(sb, sec);
+
+       sm_V(&b_sem);
+
+       return buf;
+} /* end of buf_getblk */
+
+static u8 *__buf_getblk(struct super_block *sb, sector_t 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;
+
+} /* end of __buf_getblk */
+
+void buf_modify(struct super_block *sb, sector_t 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:%llu).\n",
+            (unsigned long long)sec);
+
+       sm_V(&b_sem);
+} /* end of buf_modify */
+
+void buf_lock(struct super_block *sb, sector_t 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:%llu).\n",
+            (unsigned long long)sec);
+
+       sm_V(&b_sem);
+} /* end of buf_lock */
+
+void buf_unlock(struct super_block *sb, sector_t 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:%llu).\n",
+            (unsigned long long)sec);
+
+       sm_V(&b_sem);
+} /* end of buf_unlock */
+
+void buf_release(struct super_block *sb, sector_t 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);
+} /* end of buf_release */
+
+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);
+} /* end of buf_release_all */
+
+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);
+} /* end of buf_sync */
+
+static BUF_CACHE_T *buf_cache_find(struct super_block *sb, sector_t sec)
+{
+       s32 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;
+} /* end of buf_cache_find */
+
+static BUF_CACHE_T *buf_cache_get(struct super_block *sb, sector_t 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;
+} /* end of buf_cache_get */
+
+static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp)
+{
+       s32 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;
+} /* end of buf_cache_insert_hash */
+
+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;
+} /* end of buf_cache_remove_hash */
+
+/*======================================================================*/
+/*  Local Function Definitions                                          */
+/*======================================================================*/
+
+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;
+} /* end of buf_cache_push_to_mru */
+
+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;
+} /* end of buf_cache_push_to_lru */
+
+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);
+} /* end of buf_cache_move_to_mru */
+
+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);
+} /* end of buf_cache_move_to_lru */
diff --git a/fs/exfat/exfat_cache.h b/fs/exfat/exfat_cache.h
new file mode 100644 (file)
index 0000000..540e316
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_cache.h                                             */
+/*  PURPOSE : Header File for exFAT Cache Manager                       */
+/*            (FAT Cache & Buffer Cache)                                */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Sung-Kwan Kim] : first writing                        */
+/*                                                                      */
+/************************************************************************/
+
+#ifndef _EXFAT_CACHE_H
+#define _EXFAT_CACHE_H
+
+#include <linux/fs.h>
+#include <linux/types.h>
+#include "exfat_config.h"
+
+/*----------------------------------------------------------------------*/
+/*  Constant & Macro Definitions                                        */
+/*----------------------------------------------------------------------*/
+
+#define LOCKBIT                 0x01
+#define DIRTYBIT                0x02
+
+/*----------------------------------------------------------------------*/
+/*  Type Definitions                                                    */
+/*----------------------------------------------------------------------*/
+
+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;
+       s32                drv;
+       sector_t          sec;
+       u32               flag;
+       struct buffer_head   *buf_bh;
+} BUF_CACHE_T;
+
+/*----------------------------------------------------------------------*/
+/*  External Function Declarations                                      */
+/*----------------------------------------------------------------------*/
+
+s32  buf_init(struct super_block *sb);
+s32  buf_shutdown(struct super_block *sb);
+s32  FAT_read(struct super_block *sb, u32 loc, u32 *content);
+s32  FAT_write(struct super_block *sb, u32 loc, u32 content);
+u8 *FAT_getblk(struct super_block *sb, sector_t sec);
+void   FAT_modify(struct super_block *sb, sector_t sec);
+void   FAT_release_all(struct super_block *sb);
+void   FAT_sync(struct super_block *sb);
+u8 *buf_getblk(struct super_block *sb, sector_t sec);
+void   buf_modify(struct super_block *sb, sector_t sec);
+void   buf_lock(struct super_block *sb, sector_t sec);
+void   buf_unlock(struct super_block *sb, sector_t sec);
+void   buf_release(struct super_block *sb, sector_t sec);
+void   buf_release_all(struct super_block *sb);
+void   buf_sync(struct super_block *sb);
+
+#endif /* _EXFAT_CACHE_H */
diff --git a/fs/exfat/exfat_config.h b/fs/exfat/exfat_config.h
new file mode 100644 (file)
index 0000000..33c6525
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_config.h                                            */
+/*  PURPOSE : Header File for exFAT Configuable Policies                */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#ifndef _EXFAT_CONFIG_H
+#define _EXFAT_CONFIG_H
+
+/*======================================================================*/
+/*                                                                      */
+/*                        FFS CONFIGURATIONS                            */
+/*                  (CHANGE THIS PART IF REQUIRED)                      */
+/*                                                                      */
+/*======================================================================*/
+
+/*----------------------------------------------------------------------*/
+/* Feature Config                                                       */
+/*----------------------------------------------------------------------*/
+#ifndef CONFIG_EXFAT_DISCARD
+#define CONFIG_EXFAT_DISCARD           1       /* mount option -o discard support */
+#endif
+
+#ifndef CONFIG_EXFAT_DELAYED_SYNC
+#define CONFIG_EXFAT_DELAYED_SYNC 0
+#endif
+
+#ifndef CONFIG_EXFAT_KERNEL_DEBUG
+#define CONFIG_EXFAT_KERNEL_DEBUG      1       /* kernel debug features via ioctl */
+#endif
+
+#ifndef CONFIG_EXFAT_DEBUG_MSG
+#define CONFIG_EXFAT_DEBUG_MSG         0       /* debugging message on/off */
+#endif
+
+#ifndef CONFIG_EXFAT_DEFAULT_CODEPAGE
+#define CONFIG_EXFAT_DEFAULT_CODEPAGE  437
+#define CONFIG_EXFAT_DEFAULT_IOCHARSET "utf8"
+#endif
+
+#endif /* _EXFAT_CONFIG_H */
diff --git a/fs/exfat/exfat_core.c b/fs/exfat/exfat_core.c
new file mode 100644 (file)
index 0000000..143b721
--- /dev/null
@@ -0,0 +1,5138 @@
+/* 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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_core.c                                              */
+/*  PURPOSE : exFAT File Manager                                        */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#include <linux/version.h>
+#include <linux/param.h>
+#include <linux/log2.h>
+
+#include "exfat_bitmap.h"
+#include "exfat_config.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_core.h"
+
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+
+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
+}
+
+/*----------------------------------------------------------------------*/
+/*  Global Variable Definitions                                         */
+/*----------------------------------------------------------------------*/
+
+extern u8 uni_upcase[];
+
+/*----------------------------------------------------------------------*/
+/*  Local Variable Definitions                                          */
+/*----------------------------------------------------------------------*/
+
+static u8 name_buf[MAX_PATH_LENGTH * MAX_CHARSET_SIZE];
+
+static char *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 u8 free_bit[] = {
+       0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, /*   0 ~  19 */
+       0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, /*  20 ~  39 */
+       0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /*  40 ~  59 */
+       0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, /*  60 ~  79 */
+       0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, /*  80 ~  99 */
+       0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, /* 100 ~ 119 */
+       0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 120 ~ 139 */
+       0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, /* 140 ~ 159 */
+       0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, /* 160 ~ 179 */
+       0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, /* 180 ~ 199 */
+       0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 200 ~ 219 */
+       0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, /* 220 ~ 239 */
+       0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0                 /* 240 ~ 254 */
+};
+
+static u8 used_bit[] = {
+       0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, /*   0 ~  19 */
+       2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, /*  20 ~  39 */
+       2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, /*  40 ~  59 */
+       4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /*  60 ~  79 */
+       2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, /*  80 ~  99 */
+       3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, /* 100 ~ 119 */
+       4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, /* 120 ~ 139 */
+       3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 140 ~ 159 */
+       2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, /* 160 ~ 179 */
+       4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, /* 180 ~ 199 */
+       3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, /* 200 ~ 219 */
+       5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 220 ~ 239 */
+       4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8              /* 240 ~ 255 */
+};
+
+/*======================================================================*/
+/*  Global Function Definitions                                         */
+/*======================================================================*/
+
+/* ffsInit : roll back to the initial state of the file system */
+s32 ffsInit(void)
+{
+       s32 ret;
+
+       ret = bdev_init();
+       if (ret)
+               return ret;
+
+       ret = fs_init();
+       if (ret)
+               return ret;
+
+       return FFS_SUCCESS;
+} /* end of ffsInit */
+
+/* ffsShutdown : make free all memory-alloced global buffers */
+s32 ffsShutdown(void)
+{
+       s32 ret;
+       ret = fs_shutdown();
+       if (ret)
+               return ret;
+
+       ret = bdev_shutdown();
+       if (ret)
+               return ret;
+
+       return FFS_SUCCESS;
+} /* end of ffsShutdown */
+
+/* ffsMountVol : mount the file system volume */
+s32 ffsMountVol(struct super_block *sb)
+{
+       int i, ret;
+       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");
+
+       sm_init(&p_fs->v_sem);
+       p_fs->dev_ejected = FALSE;
+
+       /* open the block device */
+       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);
+
+       /* read Sector 0 */
+       if (sector_read(sb, 0, &tmp_bh, 1) != FFS_SUCCESS)
+               return FFS_MEDIAERR;
+
+       p_fs->PBR_sector = 0;
+
+       p_pbr = (PBR_SECTOR_T *) tmp_bh->b_data;
+
+       /* check the validity of PBR */
+       if (GET16_A(p_pbr->signature) != PBR_SIGNATURE) {
+               brelse(tmp_bh);
+               bdev_close(sb);
+               return FFS_FORMATERR;
+       }
+
+       /* fill fs_stuct */
+       for (i = 0; i < 53; i++)
+               if (p_pbr->bpb[i])
+                       break;
+
+       if (i < 53) {
+               if (GET16(p_pbr->bpb+11)) /* num_fat_sectors */
+                       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;
+} /* end of ffsMountVol */
+
+/* ffsUmountVol : umount the file system volume */
+s32 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);
+
+       /* close the block device */
+       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;
+} /* end of ffsUmountVol */
+
+/* ffsGetVolInfo : get the information of a file system volume */
+s32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info)
+{
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       if (p_fs->used_clusters == (u32) ~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; /* clu 0 & 1 */
+       info->UsedClusters = p_fs->used_clusters;
+       info->FreeClusters = info->NumClusters - info->UsedClusters;
+
+       if (p_fs->dev_ejected)
+               return FFS_MEDIAERR;
+
+       return FFS_SUCCESS;
+} /* end of ffsGetVolInfo */
+
+/* ffsSyncVol : synchronize all file system volumes */
+s32 ffsSyncVol(struct super_block *sb, s32 do_sync)
+{
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       /* synchronize the file system */
+       fs_sync(sb, do_sync);
+       fs_set_vol_flags(sb, VOL_CLEAN);
+
+       if (p_fs->dev_ejected)
+               return FFS_MEDIAERR;
+
+       return FFS_SUCCESS;
+} /* end of ffsSyncVol */
+
+/*----------------------------------------------------------------------*/
+/*  File Operation Functions                                            */
+/*----------------------------------------------------------------------*/
+
+/* ffsLookupFile : lookup a file */
+s32 ffsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid)
+{
+       s32 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);
+
+       DPRINTK("ffsLookupFile entered\n");
+
+       /* check the validity of directory name in the given pathname */
+       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;
+
+       /* search the file name for directories */
+       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;
+
+       DPRINTK("ffsLookupFile exited successfully\n");
+
+       return FFS_SUCCESS;
+} /* end of ffsLookupFile */
+
+/* ffsCreateFile : create a file */
+s32 ffsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid)
+{
+       s32 ret/*, dentry*/;
+       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);
+
+       /* check the validity of directory name in the given pathname */
+       ret = resolve_path(inode, path, &dir, &uni_name);
+       if (ret)
+               return ret;
+
+       fs_set_vol_flags(sb, VOL_DIRTY);
+
+       /* create a new file */
+       ret = create_file(inode, &dir, &uni_name, mode, fid);
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+       fs_sync(sb, 0);
+       fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+       if (p_fs->dev_ejected)
+               return FFS_MEDIAERR;
+
+       return ret;
+} /* end of ffsCreateFile */
+
+/* ffsReadFile : read data from a opened file */
+s32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount)
+{
+       s32 offset, sec_offset, clu_offset;
+       u32 clu;
+       sector_t LogSector;
+       u64 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);
+
+       /* check if the given file ID is opened */
+       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 = (s32)(fid->rwoffset >> p_fs->cluster_size_bits);
+               clu = fid->start_clu;
+
+               if (fid->flags == 0x03) {
+                       clu += clu_offset;
+               } else {
+                       /* hint information */
+                       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 = FAT_read(sb, clu); */
+                               if (FAT_read(sb, clu, &clu) == -1)
+                                       return FFS_MEDIAERR;
+
+                               clu_offset--;
+                       }
+               }
+
+               /* hint information */
+               fid->hint_last_off = (s32)(fid->rwoffset >> p_fs->cluster_size_bits);
+               fid->hint_last_clu = clu;
+
+               offset = (s32)(fid->rwoffset & (p_fs->cluster_size-1)); /* byte offset in cluster   */
+               sec_offset = offset >> p_bd->sector_size_bits;            /* sector offset in cluster */
+               offset &= p_bd->sector_size_mask;                         /* byte offset in sector    */
+
+               LogSector = START_SECTOR(clu) + sec_offset;
+
+               oneblkread = (u64)(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(((char *) buffer)+read_bytes, ((char *) tmp_bh->b_data), (s32) oneblkread);
+               } else {
+                       if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS)
+                               goto err_out;
+                       memcpy(((char *) buffer)+read_bytes, ((char *) tmp_bh->b_data)+offset, (s32) oneblkread);
+               }
+               count -= oneblkread;
+               read_bytes += oneblkread;
+               fid->rwoffset += oneblkread;
+       }
+       brelse(tmp_bh);
+
+err_out:
+       /* set the size of read bytes */
+       if (rcount != NULL)
+               *rcount = read_bytes;
+
+       if (p_fs->dev_ejected)
+               return FFS_MEDIAERR;
+
+       return FFS_SUCCESS;
+} /* end of ffsReadFile */
+
+/* ffsWriteFile : write data into a opened file */
+s32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount)
+{
+       s32 modified = FALSE, offset, sec_offset, clu_offset;
+       s32 num_clusters, num_alloc, num_alloced = (s32) ~0;
+       u32 clu, last_clu;
+       sector_t LogSector, sector = 0;
+       u64 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);
+
+       /* check if the given file ID is opened */
+       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 = (s32)((fid->size-1) >> p_fs->cluster_size_bits) + 1;
+
+       write_bytes = 0;
+
+       while (count > 0) {
+               clu_offset = (s32)(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 {
+                       /* hint information */
+                       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;
+                               /* clu = FAT_read(sb, clu); */
+                               if (FAT_read(sb, clu, &clu) == -1)
+                                       return FFS_MEDIAERR;
+
+                               clu_offset--;
+                       }
+               }
+
+               if (clu == CLUSTER_32(~0)) {
+                       num_alloc = (s32)((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;
+
+                       /* (1) allocate a chain of clusters */
+                       num_alloced = p_fs->fs_func->alloc_cluster(sb, num_alloc, &new_clu);
+                       if (num_alloced == 0)
+                               break;
+                       else if (num_alloced < 0)
+                               return FFS_MEDIAERR;
+
+                       /* (2) append to the FAT chain */
+                       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;
+               }
+
+               /* hint information */
+               fid->hint_last_off = (s32)(fid->rwoffset >> p_fs->cluster_size_bits);
+               fid->hint_last_clu = clu;
+
+               offset = (s32)(fid->rwoffset & (p_fs->cluster_size-1)); /* byte offset in cluster   */
+               sec_offset = offset >> p_bd->sector_size_bits;            /* sector offset in cluster */
+               offset &= p_bd->sector_size_mask;                         /* byte offset in sector    */
+
+               LogSector = START_SECTOR(clu) + sec_offset;
+
+               oneblkwrite = (u64)(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(((char *) tmp_bh->b_data), ((char *) buffer)+write_bytes, (s32) 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(((char *) tmp_bh->b_data)+offset, ((char *) buffer)+write_bytes, (s32) 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);
+
+       /* (3) update the direcoty entry */
+       if (p_fs->vol_type == EXFAT) {
+               es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep);
+               if (es == NULL)
+                       goto err_out;
+               ep2 = ep+1;
+       } else {
+               ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, &sector);
+               if (!ep)
+                       goto err_out;
+               ep2 = ep;
+       }
+
+       p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY);
+       p_fs->fs_func->set_entry_attr(ep, fid->attr);
+
+       if (p_fs->vol_type != EXFAT)
+               buf_modify(sb, sector);
+
+       if (modified) {
+               if (p_fs->fs_func->get_entry_flag(ep2) != fid->flags)
+                       p_fs->fs_func->set_entry_flag(ep2, fid->flags);
+
+               if (p_fs->fs_func->get_entry_size(ep2) != fid->size)
+                       p_fs->fs_func->set_entry_size(ep2, fid->size);
+
+               if (p_fs->fs_func->get_entry_clu0(ep2) != fid->start_clu)
+                       p_fs->fs_func->set_entry_clu0(ep2, fid->start_clu);
+
+               if (p_fs->vol_type != EXFAT)
+                       buf_modify(sb, sector);
+       }
+
+       if (p_fs->vol_type == EXFAT) {
+               update_dir_checksum_with_entry_set(sb, es);
+               release_entry_set(es);
+       }
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+       fs_sync(sb, 0);
+       fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+err_out:
+       /* set the size of written bytes */
+       if (wcount != NULL)
+               *wcount = write_bytes;
+
+       if (num_alloced == 0)
+               return FFS_FULL;
+
+       if (p_fs->dev_ejected)
+               return FFS_MEDIAERR;
+
+       return FFS_SUCCESS;
+} /* end of ffsWriteFile */
+
+/* ffsTruncateFile : resize the file length */
+s32 ffsTruncateFile(struct inode *inode, u64 old_size, u64 new_size)
+{
+       s32 num_clusters;
+       u32 last_clu = CLUSTER_32(0);
+       sector_t sector = 0;
+       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;
+
+       /* check if the given file ID is opened */
+       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 = (s32)((old_size-1) >> p_fs->cluster_size_bits) + 1;
+       clu.flags = fid->flags;
+
+       if (new_size > 0) {
+               num_clusters = (s32)((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);
+       }
+
+       /* (1) update the directory entry */
+       if (p_fs->vol_type == EXFAT) {
+               es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep);
+               if (es == NULL)
+                       return FFS_MEDIAERR;
+               ep2 = ep+1;
+       } else {
+               ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, &sector);
+               if (!ep)
+                       return FFS_MEDIAERR;
+               ep2 = ep;
+       }
+
+       p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY);
+       p_fs->fs_func->set_entry_attr(ep, fid->attr);
+
+       p_fs->fs_func->set_entry_size(ep2, new_size);
+       if (new_size == 0) {
+               p_fs->fs_func->set_entry_flag(ep2, 0x01);
+               p_fs->fs_func->set_entry_clu0(ep2, CLUSTER_32(0));
+       }
+
+       if (p_fs->vol_type != EXFAT)
+               buf_modify(sb, sector);
+       else {
+               update_dir_checksum_with_entry_set(sb, es);
+               release_entry_set(es);
+       }
+
+       /* (2) cut off from the FAT chain */
+       if (last_clu != CLUSTER_32(0)) {
+               if (fid->flags == 0x01)
+                       FAT_write(sb, last_clu, CLUSTER_32(~0));
+       }
+
+       /* (3) free the clusters */
+       p_fs->fs_func->free_cluster(sb, &clu, 0);
+
+       /* hint information */
+       fid->hint_last_off = -1;
+       if (fid->rwoffset > fid->size)
+               fid->rwoffset = fid->size;
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+       fs_sync(sb, 0);
+       fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+       if (p_fs->dev_ejected)
+               return FFS_MEDIAERR;
+
+       return FFS_SUCCESS;
+} /* end of ffsTruncateFile */
+
+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);
+       }
+}
+
+/* ffsMoveFile : move(rename) a old file into a new file */
+s32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry)
+{
+       s32 ret;
+       s32 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);
+       u8 *new_path = (u8 *) new_dentry->d_name.name;
+       struct inode *new_inode = new_dentry->d_inode;
+       int num_entries;
+       FILE_ID_T *new_fid = NULL;
+       s32 new_entry = 0;
+
+       /* check the validity of pointer parameters */
+       if ((new_path == NULL) || (*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;
+
+       /* check if the old file is "." or ".." */
+       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;
+
+       /* check whether new dir is existing directory and empty */
+       if (new_inode) {
+               u32 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 = (s32)((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;
+               }
+       }
+
+       /* check the validity of directory name in the given new pathname */
+       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) {
+               /* delete entries of new_dir */
+               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:
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+       fs_sync(sb, 0);
+       fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+       if (p_fs->dev_ejected)
+               return FFS_MEDIAERR;
+
+       return ret;
+} /* end of ffsMoveFile */
+
+/* ffsRemoveFile : remove a file */
+s32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid)
+{
+       s32 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);
+
+       /* (1) update the directory entry */
+       remove_file(inode, &dir, dentry);
+
+       clu_to_free.dir = fid->start_clu;
+       clu_to_free.size = (s32)((fid->size-1) >> p_fs->cluster_size_bits) + 1;
+       clu_to_free.flags = fid->flags;
+
+       /* (2) free the clusters */
+       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;
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+       fs_sync(sb, 0);
+       fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+       if (p_fs->dev_ejected)
+               return FFS_MEDIAERR;
+
+       return FFS_SUCCESS;
+} /* end of ffsRemoveFile */
+
+/* ffsSetAttr : set the attribute of a given file */
+s32 ffsSetAttr(struct inode *inode, u32 attr)
+{
+       u32 type;
+       sector_t sector = 0;
+       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);
+       u8 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;
+               }
+       }
+
+       /* get the directory entry of given file */
+       if (p_fs->vol_type == EXFAT) {
+               es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep);
+               if (es == NULL)
+                       return FFS_MEDIAERR;
+       } else {
+               ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, &sector);
+               if (!ep)
+                       return FFS_MEDIAERR;
+       }
+
+       type = p_fs->fs_func->get_entry_type(ep);
+
+       if (((type == TYPE_FILE) && (attr & ATTR_SUBDIR)) ||
+               ((type == TYPE_DIR) && (!(attr & ATTR_SUBDIR)))) {
+               s32 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);
+
+       /* set the file attribute */
+       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);
+       }
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+       fs_sync(sb, 0);
+       fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+       if (p_fs->dev_ejected)
+               return FFS_MEDIAERR;
+
+       return FFS_SUCCESS;
+} /* end of ffsSetAttr */
+
+/* ffsGetStat : get the information of a given file */
+s32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info)
+{
+       sector_t sector = 0;
+       s32 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;
+       u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0;
+
+       DPRINTK("ffsGetStat entered\n");
+
+       if (is_dir) {
+               if ((fid->dir.dir == p_fs->root_dir) &&
+                       (fid->entry == -1)) {
+                       info->Attr = ATTR_SUBDIR;
+                       memset((char *) &info->CreateTimestamp, 0, sizeof(DATE_TIME_T));
+                       memset((char *) &info->ModifyTimestamp, 0, sizeof(DATE_TIME_T));
+                       memset((char *) &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)) /* FAT16 root_dir */
+                               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;
+               }
+       }
+
+       /* get the directory entry of given file or directory */
+       if (p_fs->vol_type == EXFAT) {
+               es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_2_ENTRIES, &ep);
+               if (es == NULL)
+                       return FFS_MEDIAERR;
+               ep2 = ep+1;
+       } else {
+               ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, &sector);
+               if (!ep)
+                       return FFS_MEDIAERR;
+               ep2 = ep;
+               buf_lock(sb, sector);
+       }
+
+       /* set FILE_INFO structure using the acquired DENTRY_T */
+       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((char *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T));
+
+       *(uni_name.name) = 0x0;
+       /* XXX this is very bad for exfat cuz name is already included in es.
+        API should be revised */
+       p_fs->fs_func->get_uni_name_from_ext_entry(sb, &(fid->dir), fid->entry, uni_name.name);
+       if (*(uni_name.name) == 0x0 && p_fs->vol_type != EXFAT)
+               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 = (u64) 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;
+
+       DPRINTK("ffsGetStat exited successfully\n");
+       return FFS_SUCCESS;
+} /* end of ffsGetStat */
+
+/* ffsSetStat : set the information of a given file */
+s32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info)
+{
+       sector_t sector = 0;
+       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);
+       u8 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);
+
+       /* get the directory entry of given file or directory */
+       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 {
+               /* for other than exfat */
+               ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, &sector);
+               if (!ep)
+                       return FFS_MEDIAERR;
+               ep2 = ep;
+       }
+
+
+       p_fs->fs_func->set_entry_attr(ep, info->Attr);
+
+       /* set FILE_INFO structure using the acquired DENTRY_T */
+       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;
+} /* end of ffsSetStat */
+
+s32 ffsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu)
+{
+       s32 num_clusters, num_alloced, modified = FALSE;
+       u32 last_clu;
+       sector_t sector = 0;
+       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 = (s64)(clu_offset) << p_fs->cluster_size_bits;
+
+       if (EXFAT_I(inode)->mmu_private == 0)
+               num_clusters = 0;
+       else
+               num_clusters = (s32)((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 {
+               /* hint information */
+               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;
+
+               /* (1) allocate a cluster */
+               num_alloced = p_fs->fs_func->alloc_cluster(sb, 1, &new_clu);
+               if (num_alloced < 0)
+                       return FFS_MEDIAERR;
+               else if (num_alloced == 0)
+                       return FFS_FULL;
+
+               /* (2) append to the FAT chain */
+               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;
+                       /* get stream entry */
+                       ep++;
+               }
+
+               /* (3) update directory entry */
+               if (modified) {
+                       if (p_fs->vol_type != EXFAT) {
+                               ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, &sector);
+                               if (!ep)
+                                       return FFS_MEDIAERR;
+                       }
+
+                       if (p_fs->fs_func->get_entry_flag(ep) != fid->flags)
+                               p_fs->fs_func->set_entry_flag(ep, fid->flags);
+
+                       if (p_fs->fs_func->get_entry_clu0(ep) != fid->start_clu)
+                               p_fs->fs_func->set_entry_clu0(ep, fid->start_clu);
+
+                       if (p_fs->vol_type != EXFAT)
+                               buf_modify(sb, sector);
+               }
+
+               if (p_fs->vol_type == EXFAT) {
+                       update_dir_checksum_with_entry_set(sb, es);
+                       release_entry_set(es);
+               }
+
+               /* add number of new blocks to inode */
+               inode->i_blocks += num_alloced << (p_fs->cluster_size_bits - 9);
+       }
+
+       /* hint information */
+       fid->hint_last_off = (s32)(fid->rwoffset >> p_fs->cluster_size_bits);
+       fid->hint_last_clu = *clu;
+
+       if (p_fs->dev_ejected)
+               return FFS_MEDIAERR;
+
+       return FFS_SUCCESS;
+} /* end of ffsMapCluster */
+
+/*----------------------------------------------------------------------*/
+/*  Directory Operation Functions                                       */
+/*----------------------------------------------------------------------*/
+
+/* ffsCreateDir : create(make) a directory */
+s32 ffsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid)
+{
+       s32 ret/*, dentry*/;
+       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);
+
+       DPRINTK("ffsCreateDir entered\n");
+
+       /* check the validity of directory name in the given old pathname */
+       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);
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+       fs_sync(sb, 0);
+       fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+       if (p_fs->dev_ejected)
+               return FFS_MEDIAERR;
+
+       return ret;
+} /* end of ffsCreateDir */
+
+/* ffsReadDir : read a directory entry from the opened directory */
+s32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry)
+{
+       int i, dentry, clu_offset;
+       s32 dentries_per_clu, dentries_per_clu_bits = 0;
+       u32 type;
+       sector_t 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);
+
+       /* check if the given file ID is opened */
+       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 = (s32)(fid->size >> p_fs->cluster_size_bits);
+               dir.flags = fid->flags;
+       }
+
+       dentry = (s32) fid->rwoffset;
+
+       if (dir.dir == CLUSTER_32(0)) { /* FAT16 root_dir */
+               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 = ilog2(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 {
+                       /* hint_information */
+                       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) {
+                               /* clu.dir = FAT_read(sb, clu.dir); */
+                               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)) /* FAT16 root_dir */
+                       i = dentry % dentries_per_clu;
+               else
+                       i = dentry & (dentries_per_clu-1);
+
+               for ( ; i < dentries_per_clu; i++, dentry++) {
+                       ep = get_entry_in_dir(sb, &clu, i, &sector);
+                       if (!ep)
+                               return FFS_MEDIAERR;
+
+                       type = p_fs->fs_func->get_entry_type(ep);
+
+                       if (type == TYPE_UNUSED)
+                               break;
+
+                       if ((type != TYPE_FILE) && (type != TYPE_DIR))
+                               continue;
+
+                       buf_lock(sb, sector);
+                       dir_entry->Attr = p_fs->fs_func->get_entry_attr(ep);
+
+                       p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE);
+                       dir_entry->CreateTimestamp.Year = tm.year;
+                       dir_entry->CreateTimestamp.Month = tm.mon;
+                       dir_entry->CreateTimestamp.Day = tm.day;
+                       dir_entry->CreateTimestamp.Hour = tm.hour;
+                       dir_entry->CreateTimestamp.Minute = tm.min;
+                       dir_entry->CreateTimestamp.Second = tm.sec;
+                       dir_entry->CreateTimestamp.MilliSecond = 0;
+
+                       p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY);
+                       dir_entry->ModifyTimestamp.Year = tm.year;
+                       dir_entry->ModifyTimestamp.Month = tm.mon;
+                       dir_entry->ModifyTimestamp.Day = tm.day;
+                       dir_entry->ModifyTimestamp.Hour = tm.hour;
+                       dir_entry->ModifyTimestamp.Minute = tm.min;
+                       dir_entry->ModifyTimestamp.Second = tm.sec;
+                       dir_entry->ModifyTimestamp.MilliSecond = 0;
+
+                       memset((char *) &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 && p_fs->vol_type != EXFAT)
+                               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);
+
+                       /* hint information */
+                       if (dir.dir == CLUSTER_32(0)) { /* FAT16 root_dir */
+                       } else {
+                               fid->hint_last_off = dentry >> dentries_per_clu_bits;
+                               fid->hint_last_clu = clu.dir;
+                       }
+
+                       fid->rwoffset = (s64) ++dentry;
+
+                       if (p_fs->dev_ejected)
+                               return FFS_MEDIAERR;
+
+                       return FFS_SUCCESS;
+               }
+
+               if (dir.dir == CLUSTER_32(0))
+                       break; /* FAT16 root_dir */
+
+               if (clu.flags == 0x03) {
+                       if ((--clu.size) > 0)
+                               clu.dir++;
+                       else
+                               clu.dir = CLUSTER_32(~0);
+               } else {
+                       /* clu.dir = FAT_read(sb, clu.dir); */
+                       if (FAT_read(sb, clu.dir, &(clu.dir)) == -1)
+                               return FFS_MEDIAERR;
+               }
+       }
+
+       *(dir_entry->Name) = '\0';
+
+       fid->rwoffset = (s64) ++dentry;
+
+       if (p_fs->dev_ejected)
+               return FFS_MEDIAERR;
+
+       return FFS_SUCCESS;
+} /* end of ffsReadDir */
+
+/* ffsRemoveDir : remove a directory */
+s32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid)
+{
+       s32 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;
+
+       /* check if the file is "." or ".." */
+       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 = (s32)((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);
+
+       /* (1) update the directory entry */
+       remove_file(inode, &dir, dentry);
+
+       /* (2) free the clusters */
+       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;
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+       fs_sync(sb, 0);
+       fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+       if (p_fs->dev_ejected)
+               return FFS_MEDIAERR;
+
+       return FFS_SUCCESS;
+} /* end of ffsRemoveDir */
+
+/*======================================================================*/
+/*  Local Function Definitions                                          */
+/*======================================================================*/
+
+/*
+ *  File System Management Functions
+ */
+
+s32 fs_init(void)
+{
+       /* critical check for system requirement on size of DENTRY_T structure */
+       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;
+} /* end of fs_init */
+
+s32 fs_shutdown(void)
+{
+       return FFS_SUCCESS;
+} /* end of fs_shutdown */
+
+void fs_set_vol_flags(struct super_block *sb, u32 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, (u16) new_flag);
+
+               /* XXX duyoung
+                what can we do here? (cuz fs_set_vol_flags() is void) */
+               if ((new_flag == VOL_DIRTY) && (!buffer_dirty(p_fs->pbr_bh)))
+                       sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 1);
+               else
+                       sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 0);
+       }
+} /* end of fs_set_vol_flags */
+
+void fs_sync(struct super_block *sb, s32 do_sync)
+{
+       if (do_sync)
+               bdev_sync(sb);
+} /* end of fs_sync */
+
+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");
+       }
+}
+
+/*
+ *  Cluster Management Functions
+ */
+
+s32 clear_cluster(struct super_block *sb, u32 clu)
+{
+       sector_t s, n;
+       s32 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)) { /* FAT16 root_dir */
+               s = p_fs->root_start_sector;
+               n = p_fs->data_start_sector;
+       } else {
+               s = START_SECTOR(clu);
+               n = s + p_fs->sectors_per_clu;
+       }
+
+       for (; s < n; s++) {
+               ret = sector_read(sb, s, &tmp_bh, 0);
+               if (ret != FFS_SUCCESS)
+                       return ret;
+
+               memset((char *) tmp_bh->b_data, 0x0, p_bd->sector_size);
+               ret = sector_write(sb, s, tmp_bh, 0);
+               if (ret != FFS_SUCCESS)
+                       break;
+       }
+
+       brelse(tmp_bh);
+       return ret;
+} /* end of clear_cluster */
+
+s32 fat_alloc_cluster(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain)
+{
+       int i, num_clusters = 0;
+       u32 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 -1;
+
+               if (read_clu == CLUSTER_32(0)) {
+                       if (FAT_write(sb, new_clu, CLUSTER_32(~0)) < 0)
+                               return -1;
+                       num_clusters++;
+
+                       if (p_chain->dir == CLUSTER_32(~0))
+                               p_chain->dir = new_clu;
+                       else {
+                               if (FAT_write(sb, last_clu, new_clu) < 0)
+                                       return -1;
+                       }
+
+                       last_clu = new_clu;
+
+                       if ((--num_alloc) == 0) {
+                               p_fs->clu_srch_ptr = new_clu;
+                               if (p_fs->used_clusters != (u32) ~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 != (u32) ~0)
+               p_fs->used_clusters += num_clusters;
+
+       return num_clusters;
+} /* end of fat_alloc_cluster */
+
+s32 exfat_alloc_cluster(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain)
+{
+       s32 num_clusters = 0;
+       u32 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 -1;
+
+               num_clusters++;
+
+               if (p_chain->flags == 0x01) {
+                       if (FAT_write(sb, new_clu, CLUSTER_32(~0)) < 0)
+                               return -1;
+               }
+
+               if (p_chain->dir == CLUSTER_32(~0)) {
+                       p_chain->dir = new_clu;
+               } else {
+                       if (p_chain->flags == 0x01) {
+                               if (FAT_write(sb, last_clu, new_clu) < 0)
+                                       return -1;
+                       }
+               }
+               last_clu = new_clu;
+
+               if ((--num_alloc) == 0) {
+                       p_fs->clu_srch_ptr = hint_clu;
+                       if (p_fs->used_clusters != (u32) ~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 != (u32) ~0)
+               p_fs->used_clusters += num_clusters;
+
+       p_chain->size += num_clusters;
+       return num_clusters;
+} /* end of exfat_alloc_cluster */
+
+void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse)
+{
+       s32 num_clusters = 0;
+       u32 clu, prev;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+       int i;
+       sector_t sector;
+
+       if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0)))
+               return;
+       __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;
+
+               if (FAT_write(sb, prev, CLUSTER_32(0)) < 0)
+                       break;
+               num_clusters++;
+
+       } while (clu != CLUSTER_32(~0));
+
+       if (p_fs->used_clusters != (u32) ~0)
+               p_fs->used_clusters -= num_clusters;
+} /* end of fat_free_cluster */
+
+void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse)
+{
+       s32 num_clusters = 0;
+       u32 clu;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+       int i;
+       sector_t sector;
+
+       if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0)))
+               return;
+
+       if (p_chain->size <= 0) {
+               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 != (u32) ~0)
+               p_fs->used_clusters -= num_clusters;
+} /* end of exfat_free_cluster */
+
+u32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain)
+{
+       u32 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;
+} /* end of find_last_cluster */
+
+s32 count_num_clusters(struct super_block *sb, CHAIN_T *p_chain)
+{
+       int i, count = 0;
+       u32 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;
+} /* end of count_num_clusters */
+
+s32 fat_count_used_clusters(struct super_block *sb)
+{
+       int i, count = 0;
+       u32 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;
+} /* end of fat_count_used_clusters */
+
+s32 exfat_count_used_clusters(struct super_block *sb)
+{
+       int i, map_i, map_b, count = 0;
+       u8 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 = *(((u8 *) p_fs->vol_amap[map_i]->b_data) + map_b);
+               count += used_bit[k];
+
+               if ((++map_b) >= p_bd->sector_size) {
+                       map_i++;
+                       map_b = 0;
+               }
+       }
+
+       return count;
+} /* end of exfat_count_used_clusters */
+
+void exfat_chain_cont_cluster(struct super_block *sb, u32 chain, s32 len)
+{
+       if (len == 0)
+               return;
+
+       while (len > 1) {
+               if (FAT_write(sb, chain, chain+1) < 0)
+                       break;
+               chain++;
+               len--;
+       }
+       FAT_write(sb, chain, CLUSTER_32(~0));
+} /* end of exfat_chain_cont_cluster */
+
+/*
+ *  Allocation Bitmap Management Functions
+ */
+
+s32 load_alloc_bitmap(struct super_block *sb)
+{
+       int i, j, ret;
+       u32 map_size;
+       u32 type;
+       sector_t 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 = (u32) GET64_A(ep->size);
+
+                               p_fs->map_sectors = ((map_size-1) >> p_bd->sector_size_bits) + 1;
+
+                               p_fs->vol_amap = (struct buffer_head **) kmalloc(sizeof(struct buffer_head *) * p_fs->map_sectors, GFP_KERNEL);
+                               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) {
+                                               /*  release all buffers and free vol_amap */
+                                               i = 0;
+                                               while (i < j)
+                                                       brelse(p_fs->vol_amap[i++]);
+
+                                               if (p_fs->vol_amap)
+                                                       kfree(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;
+} /* end of load_alloc_bitmap */
+
+void free_alloc_bitmap(struct super_block *sb)
+{
+       int 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]);
+
+       if (p_fs->vol_amap)
+               kfree(p_fs->vol_amap);
+       p_fs->vol_amap = NULL;
+} /* end of free_alloc_bitmap */
+
+s32 set_alloc_bitmap(struct super_block *sb, u32 clu)
+{
+       int i, b;
+       sector_t 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;
+
+       exfat_bitmap_set((u8 *) p_fs->vol_amap[i]->b_data, b);
+
+       return sector_write(sb, sector, p_fs->vol_amap[i], 0);
+} /* end of set_alloc_bitmap */
+
+s32 clr_alloc_bitmap(struct super_block *sb, u32 clu)
+{
+       int i, b;
+       sector_t sector;
+#ifdef CONFIG_EXFAT_DISCARD
+       struct exfat_sb_info *sbi = EXFAT_SB(sb);
+       struct exfat_mount_options *opts = &sbi->options;
+       int ret;
+#endif /* CONFIG_EXFAT_DISCARD */
+       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;
+
+       exfat_bitmap_clear((u8 *) p_fs->vol_amap[i]->b_data, b);
+
+       return sector_write(sb, sector, p_fs->vol_amap[i], 0);
+
+#ifdef CONFIG_EXFAT_DISCARD
+       if (opts->discard) {
+#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 /* CONFIG_EXFAT_DISCARD */
+} /* end of clr_alloc_bitmap */
+
+u32 test_alloc_bitmap(struct super_block *sb, u32 clu)
+{
+       int i, map_i, map_b;
+       u32 clu_base, clu_free;
+       u8 k, clu_mask;
+       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 = *(((u8 *) p_fs->vol_amap[map_i]->b_data) + map_b);
+               if (clu_mask > 0) {
+                       k |= clu_mask;
+                       clu_mask = 0;
+               }
+               if (k < 0xFF) {
+                       clu_free = clu_base + free_bit[k];
+                       if (clu_free < p_fs->num_clusters)
+                               return clu_free;
+               }
+               clu_base += 8;
+
+               if (((++map_b) >= p_bd->sector_size) || (clu_base >= p_fs->num_clusters)) {
+                       if ((++map_i) >= p_fs->map_sectors) {
+                               clu_base = 2;
+                               map_i = 0;
+                       }
+                       map_b = 0;
+               }
+       }
+
+       return CLUSTER_32(~0);
+} /* end of test_alloc_bitmap */
+
+void sync_alloc_bitmap(struct super_block *sb)
+{
+       int 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]);
+} /* end of sync_alloc_bitmap */
+
+/*
+ *  Upcase table Management Functions
+ */
+s32 __load_upcase_table(struct super_block *sb, sector_t sector, u32 num_sectors, u32 utbl_checksum)
+{
+       int i, ret = FFS_ERROR;
+       u32 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;
+       sector_t end_sector = num_sectors + sector;
+
+       u8      skip = FALSE;
+       u32     index = 0;
+       u16     uni = 0;
+       u16 **upcase_table;
+
+       u32 checksum = 0;
+
+       upcase_table = p_fs->vol_utbl = (u16 **) kmalloc(UTBL_COL_COUNT * sizeof(u16 *), GFP_KERNEL);
+       if (upcase_table == NULL)
+               return FFS_MEMORYERR;
+       memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *));
+
+       while (sector < end_sector) {
+               ret = sector_read(sb, sector, &tmp_bh, 1);
+               if (ret != FFS_SUCCESS) {
+                       DPRINTK("sector read (0x%llX)fail\n", (unsigned long long)sector);
+                       goto error;
+               }
+               sector++;
+
+               for (i = 0; i < p_bd->sector_size && index <= 0xFFFF; i += 2) {
+                       uni = GET16(((u8 *) tmp_bh->b_data)+i);
+
+                       checksum = ((checksum & 1) ? 0x80000000 : 0) + (checksum >> 1) + *(((u8 *) tmp_bh->b_data)+i);
+                       checksum = ((checksum & 1) ? 0x80000000 : 0) + (checksum >> 1) + *(((u8 *) tmp_bh->b_data)+(i+1));
+
+                       if (skip) {
+                               DPRINTK("skip from 0x%X ", index);
+                               index += uni;
+                               DPRINTK("to 0x%X (amount of 0x%X)\n", index, uni);
+                               skip = FALSE;
+                       } else if (uni == index)
+                               index++;
+                       else if (uni == 0xFFFF)
+                               skip = TRUE;
+                       else { /* uni != index , uni != 0xFFFF */
+                               u16 col_index = get_col_index(index);
+
+                               if (upcase_table[col_index] == NULL) {
+                                       DPRINTK("alloc = 0x%X\n", col_index);
+                                       upcase_table[col_index] = (u16 *) kmalloc(UTBL_ROW_COUNT * sizeof(u16), GFP_KERNEL);
+                                       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;
+}
+
+s32 __load_default_upcase_table(struct super_block *sb)
+{
+       int i, ret = FFS_ERROR;
+       u32 j;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       u8      skip = FALSE;
+       u32     index = 0;
+       u16     uni = 0;
+       u16 **upcase_table;
+
+       upcase_table = p_fs->vol_utbl = (u16 **) kmalloc(UTBL_COL_COUNT * sizeof(u16 *), GFP_KERNEL);
+       if (upcase_table == NULL)
+               return FFS_MEMORYERR;
+       memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *));
+
+       for (i = 0; index <= 0xFFFF && i < NUM_UPCASE*2; i += 2) {
+               uni = GET16(uni_upcase + i);
+               if (skip) {
+                       DPRINTK("skip from 0x%X ", index);
+                       index += uni;
+                       DPRINTK("to 0x%X (amount of 0x%X)\n", index, uni);
+                       skip = FALSE;
+               } else if (uni == index)
+                       index++;
+               else if (uni == 0xFFFF)
+                       skip = TRUE;
+               else { /* uni != index , uni != 0xFFFF */
+                       u16 col_index = get_col_index(index);
+
+                       if (upcase_table[col_index] == NULL) {
+                               DPRINTK("alloc = 0x%X\n", col_index);
+                               upcase_table[col_index] = (u16 *) kmalloc(UTBL_ROW_COUNT * sizeof(u16), GFP_KERNEL);
+                               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:
+       /* FATAL error: default upcase table has error */
+       free_upcase_table(sb);
+       return ret;
+}
+
+s32 load_upcase_table(struct super_block *sb)
+{
+       int i;
+       u32 tbl_clu, tbl_size;
+       sector_t sector;
+       u32 type, num_sectors;
+       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 = (u32) GET64_A(ep->size);
+
+                       sector = START_SECTOR(tbl_clu);
+                       num_sectors = ((tbl_size-1) >> p_bd->sector_size_bits) + 1;
+                       if (__load_upcase_table(sb, sector, num_sectors, GET32_A(ep->checksum)) != FFS_SUCCESS)
+                               break;
+                       else
+                               return FFS_SUCCESS;
+               }
+               if (FAT_read(sb, clu.dir, &(clu.dir)) != 0)
+                       return FFS_MEDIAERR;
+       }
+       /* load default upcase table */
+       return __load_default_upcase_table(sb);
+} /* end of load_upcase_table */
+
+void free_upcase_table(struct super_block *sb)
+{
+       u32 i;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+       u16 **upcase_table;
+
+       upcase_table = p_fs->vol_utbl;
+       for (i = 0; i < UTBL_COL_COUNT; i++) {
+               if (upcase_table[i])
+                       kfree(upcase_table[i]);
+       }
+
+       if (p_fs->vol_utbl)
+               kfree(p_fs->vol_utbl);
+       p_fs->vol_utbl = NULL;
+} /* end of free_upcase_table */
+
+/*
+ *  Directory Entry Management Functions
+ */
+
+u32 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;
+} /* end of fat_get_entry_type */
+
+u32 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;
+} /* end of exfat_get_entry_type */
+
+void fat_set_entry_type(DENTRY_T *p_entry, u32 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;
+} /* end of fat_set_entry_type */
+
+void exfat_set_entry_type(DENTRY_T *p_entry, u32 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);
+       }
+} /* end of exfat_set_entry_type */
+
+u32 fat_get_entry_attr(DENTRY_T *p_entry)
+{
+       DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
+       return (u32) ep->attr;
+} /* end of fat_get_entry_attr */
+
+u32 exfat_get_entry_attr(DENTRY_T *p_entry)
+{
+       FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry;
+       return (u32) GET16_A(ep->attr);
+} /* end of exfat_get_entry_attr */
+
+void fat_set_entry_attr(DENTRY_T *p_entry, u32 attr)
+{
+       DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
+       ep->attr = (u8) attr;
+} /* end of fat_set_entry_attr */
+
+void exfat_set_entry_attr(DENTRY_T *p_entry, u32 attr)
+{
+       FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry;
+       SET16_A(ep->attr, (u16) attr);
+} /* end of exfat_set_entry_attr */
+
+u8 fat_get_entry_flag(DENTRY_T *p_entry)
+{
+       return 0x01;
+} /* end of fat_get_entry_flag */
+
+u8 exfat_get_entry_flag(DENTRY_T *p_entry)
+{
+       STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry;
+       return ep->flags;
+} /* end of exfat_get_entry_flag */
+
+void fat_set_entry_flag(DENTRY_T *p_entry, u8 flags)
+{
+} /* end of fat_set_entry_flag */
+
+void exfat_set_entry_flag(DENTRY_T *p_entry, u8 flags)
+{
+       STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry;
+       ep->flags = flags;
+} /* end of exfat_set_entry_flag */
+
+u32 fat_get_entry_clu0(DENTRY_T *p_entry)
+{
+       DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
+       return ((u32) GET16_A(ep->start_clu_hi) << 16) | GET16_A(ep->start_clu_lo);
+} /* end of fat_get_entry_clu0 */
+
+u32 exfat_get_entry_clu0(DENTRY_T *p_entry)
+{
+       STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry;
+       return GET32_A(ep->start_clu);
+} /* end of exfat_get_entry_clu0 */
+
+void fat_set_entry_clu0(DENTRY_T *p_entry, u32 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));
+} /* end of fat_set_entry_clu0 */
+
+void exfat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu)
+{
+       STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry;
+       SET32_A(ep->start_clu, start_clu);
+} /* end of exfat_set_entry_clu0 */
+
+u64 fat_get_entry_size(DENTRY_T *p_entry)
+{
+       DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
+       return (u64) GET32_A(ep->size);
+} /* end of fat_get_entry_size */
+
+u64 exfat_get_entry_size(DENTRY_T *p_entry)
+{
+       STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry;
+       return GET64_A(ep->valid_size);
+} /* end of exfat_get_entry_size */
+
+void fat_set_entry_size(DENTRY_T *p_entry, u64 size)
+{
+       DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
+       SET32_A(ep->size, (u32) size);
+} /* end of fat_set_entry_size */
+
+void exfat_set_entry_size(DENTRY_T *p_entry, u64 size)
+{
+       STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry;
+       SET64_A(ep->valid_size, size);
+       SET64_A(ep->size, size);
+} /* end of exfat_set_entry_size */
+
+void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode)
+{
+       u16 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);
+} /* end of fat_get_entry_time */
+
+void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode)
+{
+       u16 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);
+} /* end of exfat_get_entry_time */
+
+void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode)
+{
+       u16 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;
+       }
+} /* end of fat_set_entry_time */
+
+void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode)
+{
+       u16 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;
+       }
+} /* end of exfat_set_entry_time */
+
+s32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type,
+                                                u32 start_clu, u64 size)
+{
+       sector_t sector;
+       DOS_DENTRY_T *dos_ep;
+
+       dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, &sector);
+       if (!dos_ep)
+               return FFS_MEDIAERR;
+
+       init_dos_entry(dos_ep, type, start_clu);
+       buf_modify(sb, sector);
+
+       return FFS_SUCCESS;
+} /* end of fat_init_dir_entry */
+
+s32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type,
+                                                  u32 start_clu, u64 size)
+{
+       sector_t sector;
+       u8 flags;
+       FILE_DENTRY_T *file_ep;
+       STRM_DENTRY_T *strm_ep;
+
+       flags = (type == TYPE_FILE) ? 0x01 : 0x03;
+
+       /* we cannot use get_entry_set_in_dir here because file ep is not initialized yet */
+       file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, &sector);
+       if (!file_ep)
+               return FFS_MEDIAERR;
+
+       strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, &sector);
+       if (!strm_ep)
+               return FFS_MEDIAERR;
+
+       init_file_entry(file_ep, type);
+       buf_modify(sb, sector);
+
+       init_strm_entry(strm_ep, flags, start_clu, size);
+       buf_modify(sb, sector);
+
+       return FFS_SUCCESS;
+} /* end of exfat_init_dir_entry */
+
+s32 fat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries,
+                                                UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname)
+{
+       int i;
+       sector_t sector;
+       u8 chksum;
+       u16 *uniname = p_uniname->name;
+       DOS_DENTRY_T *dos_ep;
+       EXT_DENTRY_T *ext_ep;
+
+       dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, &sector);
+       if (!dos_ep)
+               return FFS_MEDIAERR;
+
+       dos_ep->lcase = p_dosname->name_case;
+       memcpy(dos_ep->name, p_dosname->name, DOS_NAME_LENGTH);
+       buf_modify(sb, sector);
+
+       if ((--num_entries) > 0) {
+               chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0);
+
+               for (i = 1; i < num_entries; i++) {
+                       ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, &sector);
+                       if (!ext_ep)
+                               return FFS_MEDIAERR;
+
+                       init_ext_entry(ext_ep, i, chksum, uniname);
+                       buf_modify(sb, sector);
+                       uniname += 13;
+               }
+
+               ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, &sector);
+               if (!ext_ep)
+                       return FFS_MEDIAERR;
+
+               init_ext_entry(ext_ep, i+0x40, chksum, uniname);
+               buf_modify(sb, sector);
+       }
+
+       return FFS_SUCCESS;
+} /* end of fat_init_ext_entry */
+
+s32 exfat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries,
+                                                  UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname)
+{
+       int i;
+       sector_t sector;
+       u16 *uniname = p_uniname->name;
+       FILE_DENTRY_T *file_ep;
+       STRM_DENTRY_T *strm_ep;
+       NAME_DENTRY_T *name_ep;
+
+       file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, &sector);
+       if (!file_ep)
+               return FFS_MEDIAERR;
+
+       file_ep->num_ext = (u8)(num_entries - 1);
+       buf_modify(sb, sector);
+
+       strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, &sector);
+       if (!strm_ep)
+               return FFS_MEDIAERR;
+
+       strm_ep->name_len = p_uniname->name_len;
+       SET16_A(strm_ep->name_hash, p_uniname->name_hash);
+       buf_modify(sb, sector);
+
+       for (i = 2; i < num_entries; i++) {
+               name_ep = (NAME_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+i, &sector);
+               if (!name_ep)
+                       return FFS_MEDIAERR;
+
+               init_name_entry(name_ep, uniname);
+               buf_modify(sb, sector);
+               uniname += 15;
+       }
+
+       update_dir_checksum(sb, p_dir, entry);
+
+       return FFS_SUCCESS;
+} /* end of exfat_init_ext_entry */
+
+void init_dos_entry(DOS_DENTRY_T *ep, u32 type, u32 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;
+} /* end of init_dos_entry */
+
+void init_ext_entry(EXT_DENTRY_T *ep, s32 order, u8 chksum, u16 *uniname)
+{
+       int i;
+       u8 end = FALSE;
+
+       fat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND);
+       ep->order = (u8) 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);
+               }
+       }
+} /* end of init_ext_entry */
+
+void init_file_entry(FILE_DENTRY_T *ep, u32 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;
+} /* end of init_file_entry */
+
+void init_strm_entry(STRM_DENTRY_T *ep, u8 flags, u32 start_clu, u64 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);
+} /* end of init_strm_entry */
+
+void init_name_entry(NAME_DENTRY_T *ep, u16 *uniname)
+{
+       int 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++;
+       }
+} /* end of init_name_entry */
+
+void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries)
+{
+       int i;
+       sector_t sector;
+       DENTRY_T *ep;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       for (i = num_entries-1; i >= order; i--) {
+               ep = get_entry_in_dir(sb, p_dir, entry-i, &sector);
+               if (!ep)
+                       return;
+
+               p_fs->fs_func->set_entry_type(ep, TYPE_DELETED);
+               buf_modify(sb, sector);
+       }
+} /* end of fat_delete_dir_entry */
+
+void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries)
+{
+       int i;
+       sector_t sector;
+       DENTRY_T *ep;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       for (i = order; i < num_entries; i++) {
+               ep = get_entry_in_dir(sb, p_dir, entry+i, &sector);
+               if (!ep)
+                       return;
+
+               p_fs->fs_func->set_entry_type(ep, TYPE_DELETED);
+               buf_modify(sb, sector);
+       }
+} /* end of exfat_delete_dir_entry */
+
+void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, s32 entry)
+{
+       int i, num_entries;
+       sector_t sector;
+       u16 chksum;
+       FILE_DENTRY_T *file_ep;
+       DENTRY_T *ep;
+
+       file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, &sector);
+       if (!file_ep)
+               return;
+
+       buf_lock(sb, sector);
+
+       num_entries = (s32) file_ep->num_ext + 1;
+       chksum = calc_checksum_2byte((void *) file_ep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
+
+       for (i = 1; i < num_entries; i++) {
+               ep = get_entry_in_dir(sb, p_dir, entry+i, NULL);
+               if (!ep) {
+                       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);
+} /* end of update_dir_checksum */
+
+void update_dir_checksum_with_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es)
+{
+       DENTRY_T *ep;
+       u16 chksum = 0;
+       s32 chksum_type = CS_DIR_ENTRY, i;
+
+       ep = (DENTRY_T *)&(es->__buf);
+       for (i = 0; i < es->num_entries; i++) {
+               DPRINTK("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 s32 _walk_fat_chain(struct super_block *sb, CHAIN_T *p_dir, s32 byte_offset, u32 *clu)
+{
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+       s32 clu_offset;
+       u32 cur_clu;
+
+       clu_offset = byte_offset >> p_fs->cluster_size_bits;
+       cur_clu = p_dir->dir;
+
+       if (p_dir->flags == 0x03) {
+               cur_clu += clu_offset;
+       } else {
+               while (clu_offset > 0) {
+                       if (FAT_read(sb, cur_clu, &cur_clu) == -1)
+                               return FFS_MEDIAERR;
+                       clu_offset--;
+               }
+       }
+
+       if (clu)
+               *clu = cur_clu;
+       return FFS_SUCCESS;
+}
+s32 find_location(struct super_block *sb, CHAIN_T *p_dir, s32 entry, sector_t *sector, s32 *offset)
+{
+       s32 off, ret;
+       u32 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)) { /* FAT16 root_dir */
+               *offset = off & p_bd->sector_size_mask;
+               *sector = off >> p_bd->sector_size_bits;
+               *sector += p_fs->root_start_sector;
+       } else {
+               ret = _walk_fat_chain(sb, p_dir, off, &clu);
+               if (ret != FFS_SUCCESS)
+                       return ret;
+
+               off &= p_fs->cluster_size - 1;  /* byte offset in cluster */
+
+               *offset = off & p_bd->sector_size_mask; /* byte offset in sector    */
+               *sector = off >> p_bd->sector_size_bits;        /* sector offset in cluster */
+               *sector += START_SECTOR(clu);
+       }
+       return FFS_SUCCESS;
+} /* end of find_location */
+
+DENTRY_T *get_entry_with_sector(struct super_block *sb, sector_t sector, s32 offset)
+{
+       u8 *buf;
+
+       buf = buf_getblk(sb, sector);
+
+       if (buf == NULL)
+               return NULL;
+
+       return (DENTRY_T *)(buf + offset);
+} /* end of get_entry_with_sector */
+
+DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, sector_t *sector)
+{
+       s32 off;
+       sector_t sec;
+       u8 *buf;
+
+       if (find_location(sb, p_dir, entry, &sec, &off) != FFS_SUCCESS)
+               return NULL;
+
+       buf = buf_getblk(sb, sec);
+
+       if (buf == NULL)
+               return NULL;
+
+       if (sector != NULL)
+               *sector = sec;
+       return (DENTRY_T *)(buf + off);
+} /* end of get_entry_in_dir */
+
+
+/* returns a set of dentries for a file or dir.
+ * Note that this is a copy (dump) of dentries so that user should call write_entry_set()
+ * to apply changes made in this entry set to the real device.
+ * in:
+ *   sb+p_dir+entry: indicates a file/dir
+ *   type:  specifies how many dentries should be included.
+ * out:
+ *   file_ep: will point the first dentry(= file dentry) on success
+ * return:
+ *   pointer of entry set on success,
+ *   NULL on failure.
+ */
+
+#define ES_MODE_STARTED                                0
+#define ES_MODE_GET_FILE_ENTRY                 1
+#define ES_MODE_GET_STRM_ENTRY                 2
+#define ES_MODE_GET_NAME_ENTRY                 3
+#define ES_MODE_GET_CRITICAL_SEC_ENTRY         4
+ENTRY_SET_CACHE_T *get_entry_set_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, DENTRY_T **file_ep)
+{
+       s32 off, ret, byte_offset;
+       u32 clu = 0;
+       sector_t sec;
+       u32 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;
+       u8 *buf;
+       u8 num_entries;
+       s32 mode = ES_MODE_STARTED;
+
+       DPRINTK("get_entry_set_in_dir entered\n");
+       DPRINTK("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;  /* byte offset in cluster */
+
+       off = byte_offset & p_bd->sector_size_mask;     /* byte offset in sector    */
+       sec = byte_offset >> p_bd->sector_size_bits;    /* sector offset in cluster */
+       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;
+
+       DPRINTK("trying to kmalloc %zx bytes for %d entries\n", offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries)  * sizeof(DENTRY_T), num_entries);
+       es = kmalloc(offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries)  * sizeof(DENTRY_T), GFP_KERNEL);
+       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) {
+               /* instead of copying whole sector, we will check every entry.
+                * this will provide minimum stablity and consistancy.
+                */
+
+               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;
+               }
+
+               memcpy(pos, ep, sizeof(DENTRY_T));
+
+               if (--num_entries == 0)
+                       break;
+
+               if (((off + DENTRY_SIZE) & p_bd->sector_size_mask) < (off &  p_bd->sector_size_mask)) {
+                       /* get the next sector */
+                       if (IS_LAST_SECTOR_IN_CLUSTER(sec)) {
+                               if (es->alloc_flag == 0x03) {
+                                       clu++;
+                               } else {
+                                       if (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);
+
+       DPRINTK("es sec %llu offset %d flags %d, num_entries %u buf ptr %p\n",
+                  (unsigned long long)es->sector, es->offset, es->alloc_flag,
+                  es->num_entries, &(es->__buf));
+       DPRINTK("get_entry_set_in_dir exited %p\n", es);
+       return es;
+err_out:
+       DPRINTK("get_entry_set_in_dir exited NULL (es %p)\n", es);
+       if (es)
+               kfree(es);
+       return NULL;
+}
+
+void release_entry_set(ENTRY_SET_CACHE_T *es)
+{
+       DPRINTK("release_entry_set %p\n", es);
+       if (es)
+               kfree(es);
+}
+
+
+static s32 __write_partial_entries_in_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es, sector_t sec, s32 off, u32 count)
+{
+       s32 num_entries, buf_off = (off - es->offset);
+       u32 remaining_byte_in_sector, copy_entries;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+       BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
+       u32 clu;
+       u8 *buf, *esbuf = (u8 *)&(es->__buf);
+
+       DPRINTK("__write_partial_entries_in_entry_set entered\n");
+       DPRINTK("es %p sec %llu off %d count %d\n", es, (unsigned long long)sec, off, count);
+       num_entries = count;
+
+       while (num_entries) {
+               /* white per sector base */
+               remaining_byte_in_sector = (1 << p_bd->sector_size_bits) - off;
+               copy_entries = MIN(remaining_byte_in_sector >> DENTRY_SIZE_BITS , num_entries);
+               buf = buf_getblk(sb, sec);
+               if (buf == NULL)
+                       goto err_out;
+               DPRINTK("es->buf %p buf_off %u\n", esbuf, buf_off);
+               DPRINTK("copying %d entries from %p to sector %llu\n", copy_entries, (esbuf + buf_off), (unsigned long long)sec);
+               memcpy(buf + off, esbuf + buf_off, copy_entries << DENTRY_SIZE_BITS);
+               buf_modify(sb, sec);
+               num_entries -= copy_entries;
+
+               if (num_entries) {
+                       /* get next sector */
+                       if (IS_LAST_SECTOR_IN_CLUSTER(sec)) {
+                               clu = GET_CLUSTER_FROM_SECTOR(sec);
+                               if (es->alloc_flag == 0x03) {
+                                       clu++;
+                               } else {
+                                       if (FAT_read(sb, clu, &clu) == -1)
+                                               goto err_out;
+                               }
+                               sec = START_SECTOR(clu);
+                       } else {
+                               sec++;
+                       }
+                       off = 0;
+                       buf_off += copy_entries << DENTRY_SIZE_BITS;
+               }
+       }
+
+       DPRINTK("__write_partial_entries_in_entry_set exited successfully\n");
+       return FFS_SUCCESS;
+err_out:
+       DPRINTK("__write_partial_entries_in_entry_set failed\n");
+       return FFS_ERROR;
+}
+
+/* write back all entries in entry set */
+s32 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);
+}
+
+/* write back some entries in entry set */
+s32 write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, u32 count)
+{
+       s32 ret, byte_offset, off;
+       u32 clu=0;
+       sector_t sec;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+       BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
+       CHAIN_T dir;
+
+       /* vaidity check */
+       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;          /* XXX */
+
+       byte_offset = (es->sector - START_SECTOR(dir.dir)) << p_bd->sector_size_bits;
+       byte_offset += ((void **)ep - &(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;  /* byte offset in cluster */
+       off = byte_offset & p_bd->sector_size_mask;     /* byte offset in sector    */
+       sec = byte_offset >> p_bd->sector_size_bits;    /* sector offset in cluster */
+       sec += START_SECTOR(clu);
+       return __write_partial_entries_in_entry_set(sb, es, sec, off, count);
+}
+
+/* search EMPTY CONTINUOUS "num_entries" entries */
+s32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, s32 num_entries)
+{
+       int i, dentry, num_empty = 0;
+       s32 dentries_per_clu;
+       u32 type;
+       CHAIN_T clu;
+       DENTRY_T *ep;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
+               dentries_per_clu = p_fs->dentries_in_root;
+       else
+               dentries_per_clu = p_fs->dentries_per_clu;
+
+       if (p_fs->hint_uentry.dir == p_dir->dir) {
+               if (p_fs->hint_uentry.entry == -1)
+                       return -1;
+
+               clu.dir = p_fs->hint_uentry.clu.dir;
+               clu.size = p_fs->hint_uentry.clu.size;
+               clu.flags = p_fs->hint_uentry.clu.flags;
+
+               dentry = p_fs->hint_uentry.entry;
+       } else {
+               p_fs->hint_uentry.entry = -1;
+
+               clu.dir = p_dir->dir;
+               clu.size = p_dir->size;
+               clu.flags = p_dir->flags;
+
+               dentry = 0;
+       }
+
+       while (clu.dir != CLUSTER_32(~0)) {
+               if (p_fs->dev_ejected)
+                       break;
+
+               if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
+                       i = dentry % dentries_per_clu;
+               else
+                       i = dentry & (dentries_per_clu-1);
+
+               for (; i < dentries_per_clu; i++, dentry++) {
+                       ep = get_entry_in_dir(sb, &clu, i, NULL);
+                       if (!ep)
+                               return -1;
+
+                       type = 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; /* FAT16 root_dir */
+
+               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;
+} /* end of search_deleted_or_unused_entry */
+
+s32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, s32 num_entries)
+{
+       s32 ret, dentry;
+       u32 last_clu;
+       sector_t sector;
+       u64 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)) /* FAT16 root_dir */
+               return search_deleted_or_unused_entry(sb, p_dir, num_entries);
+
+       while ((dentry = search_deleted_or_unused_entry(sb, p_dir, num_entries)) < 0) {
+               if (p_fs->dev_ejected)
+                       break;
+
+               if (p_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;
+
+               /* (1) allocate a cluster */
+               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;
+
+               /* (2) append to the FAT chain */
+               if (clu.flags != p_dir->flags) {
+                       exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size);
+                       p_dir->flags = 0x01;
+                       p_fs->hint_uentry.clu.flags = 0x01;
+               }
+               if (clu.flags == 0x01)
+                       if (FAT_write(sb, last_clu, clu.dir) < 0)
+                               return -1;
+
+               if (p_fs->hint_uentry.entry == -1) {
+                       p_fs->hint_uentry.dir = p_dir->dir;
+                       p_fs->hint_uentry.entry = p_dir->size << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS);
+
+                       p_fs->hint_uentry.clu.dir = clu.dir;
+                       p_fs->hint_uentry.clu.size = 0;
+                       p_fs->hint_uentry.clu.flags = clu.flags;
+               }
+               p_fs->hint_uentry.clu.size++;
+               p_dir->size++;
+
+               /* (3) update the directory entry */
+               if (p_fs->vol_type == EXFAT) {
+                       if (p_dir->dir != p_fs->root_dir) {
+                               size += p_fs->cluster_size;
+
+                               ep = get_entry_in_dir(sb, &(fid->dir), fid->entry+1, &sector);
+                               if (!ep)
+                                       return -1;
+                               p_fs->fs_func->set_entry_size(ep, size);
+                               p_fs->fs_func->set_entry_flag(ep, p_dir->flags);
+                               buf_modify(sb, sector);
+
+                               update_dir_checksum(sb, &(fid->dir), fid->entry);
+                       }
+               }
+
+               i_size_write(inode, i_size_read(inode)+p_fs->cluster_size);
+               EXFAT_I(inode)->mmu_private += p_fs->cluster_size;
+               EXFAT_I(inode)->fid.size += p_fs->cluster_size;
+               EXFAT_I(inode)->fid.flags = p_dir->flags;
+               inode->i_blocks += 1 << (p_fs->cluster_size_bits - 9);
+       }
+
+       return dentry;
+} /* end of find_empty_entry */
+
+/* return values of fat_find_dir_entry()
+   >= 0 : return dir entiry position with the name in dir
+   -1 : (root dir, ".") it is the root dir itself
+   -2 : entry with the name does not exist */
+s32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type)
+{
+       int i, dentry = 0, lossy = FALSE, len;
+       s32 order = 0, is_feasible_entry = TRUE, has_ext_entry = FALSE;
+       s32 dentries_per_clu;
+       u32 entry_type;
+       u16 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, (u16 *) UNI_CUR_DIR_NAME)) ||
+                       (!nls_uniname_cmp(sb, p_uniname->name, (u16 *) UNI_PAR_DIR_NAME)))
+                       return -1; // special case, root directory itself
+       }
+
+       if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
+               dentries_per_clu = p_fs->dentries_in_root;
+       else
+               dentries_per_clu = p_fs->dentries_per_clu;
+
+       clu.dir = p_dir->dir;
+       clu.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 = (s32)(ext_ep->order - 0x40);
+                                               uniname = p_uniname->name + 13 * (order-1);
+                                       } else {
+                                               order = (s32) 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; /* FAT16 root_dir */
+
+               if (FAT_read(sb, clu.dir, &(clu.dir)) != 0)
+                       return -2;
+       }
+
+       return -2;
+} /* end of fat_find_dir_entry */
+
+/* return values of exfat_find_dir_entry()
+   >= 0 : return dir entiry position with the name in dir
+   -1 : (root dir, ".") it is the root dir itself
+   -2 : entry with the name does not exist */
+s32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type)
+{
+       int i = 0, dentry = 0, num_ext_entries = 0, len, step;
+       s32 order = 0, is_feasible_entry = FALSE;
+       s32 dentries_per_clu, num_empty = 0;
+       u32 entry_type;
+       u16 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, (u16 *) UNI_CUR_DIR_NAME)) ||
+                       (!nls_uniname_cmp(sb, p_uniname->name, (u16 *) UNI_PAR_DIR_NAME)))
+                       return -1; // special case, root directory itself
+       }
+
+       if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
+               dentries_per_clu = p_fs->dentries_in_root;
+       else
+               dentries_per_clu = p_fs->dentries_per_clu;
+
+       clu.dir = p_dir->dir;
+       clu.size = p_dir->size;
+       clu.flags = p_dir->flags;
+
+       p_fs->hint_uentry.dir = p_dir->dir;
+       p_fs->hint_uentry.entry = -1;
+
+       while (clu.dir != CLUSTER_32(~0)) {
+               if (p_fs->dev_ejected)
+                       break;
+
+               while (i < dentries_per_clu) {
+                       ep = get_entry_in_dir(sb, &clu, i, NULL);
+                       if (!ep)
+                               return -2;
+
+                       entry_type = p_fs->fs_func->get_entry_type(ep);
+                       step = 1;
+
+                       if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) {
+                               is_feasible_entry = FALSE;
+
+                               if (p_fs->hint_uentry.entry == -1) {
+                                       num_empty++;
+
+                                       if (num_empty == 1) {
+                                               p_fs->hint_uentry.clu.dir = clu.dir;
+                                               p_fs->hint_uentry.clu.size = clu.size;
+                                               p_fs->hint_uentry.clu.flags = clu.flags;
+                                       }
+                                       if ((num_empty >= num_entries) || (entry_type == TYPE_UNUSED))
+                                               p_fs->hint_uentry.entry = dentry - (num_empty-1);
+                               }
+
+                               if (entry_type == TYPE_UNUSED)
+                                       return -2;
+                       } else {
+                               num_empty = 0;
+
+                               if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) {
+                                       file_ep = (FILE_DENTRY_T *) ep;
+                                       if ((type == TYPE_ALL) || (type == entry_type)) {
+                                               num_ext_entries = file_ep->num_ext;
+                                               is_feasible_entry = TRUE;
+                                       } else {
+                                               is_feasible_entry = FALSE;
+                                               step = file_ep->num_ext + 1;
+                                       }
+                               } else if (entry_type == TYPE_STREAM) {
+                                       if (is_feasible_entry) {
+                                               strm_ep = (STRM_DENTRY_T *) ep;
+                                               if (p_uniname->name_hash == GET16_A(strm_ep->name_hash) &&
+                                                   p_uniname->name_len == strm_ep->name_len) {
+                                                       order = 1;
+                                               } else {
+                                                       is_feasible_entry = FALSE;
+                                                       step = num_ext_entries;
+                                               }
+                                       }
+                               } else if (entry_type == TYPE_EXTEND) {
+                                       if (is_feasible_entry) {
+                                               name_ep = (NAME_DENTRY_T *) ep;
+
+                                               if ((++order) == 2)
+                                                       uniname = p_uniname->name;
+                                               else
+                                                       uniname += 15;
+
+                                               len = extract_uni_name_from_name_entry(name_ep, entry_uniname, order);
+
+                                               unichar = *(uniname+len);
+                                               *(uniname+len) = 0x0;
+
+                                               if (nls_uniname_cmp(sb, uniname, entry_uniname)) {
+                                                       is_feasible_entry = FALSE;
+                                                       step = num_ext_entries - order + 1;
+                                               } else if (order == num_ext_entries) {
+                                                       p_fs->hint_uentry.dir = CLUSTER_32(~0);
+                                                       p_fs->hint_uentry.entry = -1;
+                                                       return dentry - (num_ext_entries);
+                                               }
+
+                                               *(uniname+len) = unichar;
+                                       }
+                               } else {
+                                       is_feasible_entry = FALSE;
+                               }
+                       }
+
+                       i += step;
+                       dentry += step;
+               }
+
+               i -= dentries_per_clu;
+
+               if (p_dir->dir == CLUSTER_32(0))
+                       break; /* FAT16 root_dir */
+
+               if (clu.flags == 0x03) {
+                       if ((--clu.size) > 0)
+                               clu.dir++;
+                       else
+                               clu.dir = CLUSTER_32(~0);
+               } else {
+                       if (FAT_read(sb, clu.dir, &(clu.dir)) != 0)
+                               return -2;
+               }
+       }
+
+       return -2;
+} /* end of exfat_find_dir_entry */
+
+/* returns -1 on error */
+s32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry)
+{
+       s32 count = 0;
+       u8 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;
+} /* end of fat_count_ext_entries */
+
+/* returns -1 on error */
+s32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry)
+{
+       int i, count = 0;
+       u32 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;
+} /* end of exfat_count_ext_entries */
+
+/* returns -1 on error */
+s32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, u32 type)
+{
+       int i, count = 0;
+       s32 dentries_per_clu;
+       u32 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)) /* FAT16 root_dir */
+               dentries_per_clu = p_fs->dentries_in_root;
+       else
+               dentries_per_clu = p_fs->dentries_per_clu;
+
+       clu.dir = p_dir->dir;
+       clu.size = p_dir->size;
+       clu.flags = p_dir->flags;
+
+       while (clu.dir != CLUSTER_32(~0)) {
+               if (p_fs->dev_ejected)
+                       break;
+
+               for (i = 0; i < dentries_per_clu; i++) {
+                       ep = get_entry_in_dir(sb, &clu, i, NULL);
+                       if (!ep)
+                               return -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; /* FAT16 root_dir */
+
+               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;
+} /* end of count_dos_name_entries */
+
+bool is_dir_empty(struct super_block *sb, CHAIN_T *p_dir)
+{
+       int i, count = 0;
+       s32 dentries_per_clu;
+       u32 type;
+       CHAIN_T clu;
+       DENTRY_T *ep;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
+               dentries_per_clu = p_fs->dentries_in_root;
+       else
+               dentries_per_clu = p_fs->dentries_per_clu;
+
+       clu.dir = p_dir->dir;
+       clu.size = p_dir->size;
+       clu.flags = p_dir->flags;
+
+       while (clu.dir != CLUSTER_32(~0)) {
+               if (p_fs->dev_ejected)
+                       break;
+
+               for (i = 0; i < dentries_per_clu; i++) {
+                       ep = get_entry_in_dir(sb, &clu, i, NULL);
+                       if (!ep)
+                               break;
+
+                       type = 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)) { /* FAT16 root_dir */
+                               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; /* FAT16 root_dir */
+
+               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;
+} /* end of is_dir_empty */
+
+/*
+ *  Name Conversion Functions
+ */
+
+/* input  : dir, uni_name
+   output : num_of_entry, dos_name(format : aaaaaa~1.bbb) */
+s32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 *entries, DOS_NAME_T *p_dosname)
+{
+       s32 ret, num_entries, lossy = FALSE;
+       char **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;
+} /* end of get_num_entries_and_dos_name */
+
+void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, u8 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);
+} /* end of get_uni_name_from_dos_entry */
+
+void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname)
+{
+       int 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;
+       }
+} /* end of fat_get_uni_name_from_ext_entry */
+
+void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname)
+{
+       int 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;
+
+       /*
+       * First entry  : file entry
+       * Second entry : stream-extension entry
+       * Third entry  : first file-name entry
+       * So, the index of first file-name dentry should start from 2.
+       */
+       for (i = 2; i < es->num_entries; i++, ep++) {
+               if (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);
+} /* end of exfat_get_uni_name_from_ext_entry */
+
+s32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, u16 *uniname, s32 order)
+{
+       int 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; /* uniname[MAX_NAME_LENGTH-1] */
+               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;
+
+} /* end of extract_uni_name_from_ext_entry */
+
+s32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, u16 *uniname, s32 order)
+{
+       int i, len = 0;
+
+       for (i = 0; i < 30; i += 2) {
+               *uniname = GET16_A(ep->unicode_0_14+i);
+               if (*uniname == 0x0)
+                       return len;
+               uniname++;
+               len++;
+       }
+
+       *uniname = 0x0;
+       return len;
+
+} /* end of extract_uni_name_from_name_entry */
+
+s32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname)
+{
+       int i, j, count = 0, count_begin = FALSE;
+       s32 dentries_per_clu;
+       u32 type;
+       u8 bmap[128/* 1 ~ 1023 */];
+       CHAIN_T clu;
+       DOS_DENTRY_T *ep;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       memset(bmap, 0, sizeof bmap);
+       exfat_bitmap_set(bmap, 0);
+
+       if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
+               dentries_per_clu = p_fs->dentries_in_root;
+       else
+               dentries_per_clu = p_fs->dentries_per_clu;
+
+       clu.dir = p_dir->dir;
+       clu.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))
+                               exfat_bitmap_set(bmap, count);
+               }
+
+               if (p_dir->dir == CLUSTER_32(0))
+                       break; /* FAT16 root_dir */
+
+               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 (exfat_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);
+
+       /* Now dos_name has DOS~????.EXT */
+       return FFS_SUCCESS;
+} /* end of generate_dos_name */
+
+void fat_attach_count_to_dos_name(u8 *dosname, s32 count)
+{
+       int i, j, length;
+       char str_count[6];
+
+       snprintf(str_count, sizeof str_count, "~%d", 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] = (u8) str_count[j];
+
+       if (i == 7)
+               dosname[7] = ' ';
+
+} /* end of attach_count_to_dos_name */
+
+s32 fat_calc_num_entries(UNI_NAME_T *p_uniname)
+{
+       s32 len;
+
+       len = p_uniname->name_len;
+       if (len == 0)
+               return 0;
+
+       /* 1 dos name entry + extended entries */
+       return (len-1) / 13 + 2;
+
+} /* end of calc_num_enties */
+
+s32 exfat_calc_num_entries(UNI_NAME_T *p_uniname)
+{
+       s32 len;
+
+       len = p_uniname->name_len;
+       if (len == 0)
+               return 0;
+
+       /* 1 file entry + 1 stream entry + name entries */
+       return (len-1) / 15 + 3;
+
+} /* end of exfat_calc_num_enties */
+
+u8 calc_checksum_1byte(void *data, s32 len, u8 chksum)
+{
+       int i;
+       u8 *c = (u8 *) data;
+
+       for (i = 0; i < len; i++, c++)
+               chksum = (((chksum & 1) << 7) | ((chksum & 0xFE) >> 1)) + *c;
+
+       return chksum;
+} /* end of calc_checksum_1byte */
+
+u16 calc_checksum_2byte(void *data, s32 len, u16 chksum, s32 type)
+{
+       int i;
+       u8 *c = (u8 *) data;
+
+       switch (type) {
+       case CS_DIR_ENTRY:
+               for (i = 0; i < len; i++, c++) {
+                       if ((i == 2) || (i == 3))
+                               continue;
+                       chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (u16) *c;
+               }
+               break;
+       default
+                       :
+               for (i = 0; i < len; i++, c++)
+                       chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (u16) *c;
+       }
+
+       return chksum;
+} /* end of calc_checksum_2byte */
+
+u32 calc_checksum_4byte(void *data, s32 len, u32 chksum, s32 type)
+{
+       int i;
+       u8 *c = (u8 *) 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)) + (u32) *c;
+               }
+               break;
+       default
+                       :
+               for (i = 0; i < len; i++, c++)
+                       chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (u32) *c;
+       }
+
+       return chksum;
+} /* end of calc_checksum_4byte */
+
+/*
+ *  Name Resolution Functions
+ */
+
+/* return values of resolve_path()
+   > 0 : return the length of the path
+   < 0 : return error */
+s32 resolve_path(struct inode *inode, char *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname)
+{
+       s32 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 = (s32)(fid->size >> p_fs->cluster_size_bits);
+       p_dir->flags = fid->flags;
+
+       return FFS_SUCCESS;
+}
+
+/*
+ *  File Operation Functions
+ */
+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,
+};
+
+
+s32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr)
+{
+       s32 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 = ilog2(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;
+       /* because the cluster index starts with 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 = (u32) ~0;
+
+       p_fs->fs_func = &fat_fs_func;
+
+       return FFS_SUCCESS;
+} /* end of fat16_mount */
+
+s32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr)
+{
+       s32 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 = ilog2(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;
+       /* because the cluster index starts with 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 = (u32) ~0;
+
+       p_fs->fs_func = &fat_fs_func;
+
+       return FFS_SUCCESS;
+} /* end of fat32_mount */
+
+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,
+};
+
+s32 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;
+       /* because the cluster index starts with 2 */
+
+       p_fs->vol_type = EXFAT;
+       p_fs->vol_id = GET32(p_bpb->vol_serial);
+
+       p_fs->root_dir = GET32(p_bpb->root_cluster);
+       p_fs->dentries_in_root = 0;
+       p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS);
+
+       p_fs->vol_flag = (u32) GET16(p_bpb->vol_flags);
+       p_fs->clu_srch_ptr = 2;
+       p_fs->used_clusters = (u32) ~0;
+
+       p_fs->fs_func = &exfat_fs_func;
+
+       return FFS_SUCCESS;
+} /* end of exfat_mount */
+
+s32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid)
+{
+       s32 ret, dentry, num_entries;
+       u64 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;
+
+       /* find_empty_entry must be called before alloc_cluster */
+       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;
+
+       /* (1) allocate a cluster */
+       ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu);
+       if (ret < 0)
+               return FFS_MEDIAERR;
+       else if (ret == 0)
+               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;
+
+               /* initialize the . and .. entry
+                  Information for . points to itself
+                  Information for .. points to parent dir */
+
+               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;
+       }
+
+       /* (2) update the directory entry */
+       /* make sub-dir entry in parent directory */
+       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;
+} /* end of create_dir */
+
+s32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, u8 mode, FILE_ID_T *fid)
+{
+       s32 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;
+
+       /* find_empty_entry must be called before alloc_cluster() */
+       dentry = find_empty_entry(inode, p_dir, num_entries);
+       if (dentry < 0)
+               return FFS_FULL;
+
+       /* (1) update the directory entry */
+       /* fill the dos name directory entry information of the created file.
+          the first cluster is not determined yet. (0) */
+       ret = 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;
+} /* end of create_file */
+
+void remove_file(struct inode *inode, CHAIN_T *p_dir, s32 entry)
+{
+       s32 num_entries;
+       sector_t sector;
+       DENTRY_T *ep;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       ep = get_entry_in_dir(sb, p_dir, entry, &sector);
+       if (!ep)
+               return;
+
+       buf_lock(sb, sector);
+
+       /* buf_lock() before call count_ext_entries() */
+       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);
+
+       /* (1) update the directory entry */
+       p_fs->fs_func->delete_dir_entry(sb, p_dir, entry, 0, num_entries);
+} /* end of remove_file */
+
+s32 rename_file(struct inode *inode, CHAIN_T *p_dir, s32 oldentry, UNI_NAME_T *p_uniname, FILE_ID_T *fid)
+{
+       s32 ret, newentry = -1, num_old_entries, num_new_entries;
+       sector_t sector_old, sector_new;
+       DOS_NAME_T dos_name;
+       DENTRY_T *epold, *epnew;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       epold = get_entry_in_dir(sb, p_dir, oldentry, &sector_old);
+       if (!epold)
+               return FFS_MEDIAERR;
+
+       buf_lock(sb, sector_old);
+
+       /* buf_lock() before call count_ext_entries() */
+       num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, oldentry, epold);
+       if (num_old_entries < 0) {
+               buf_unlock(sb, sector_old);
+               return FFS_MEDIAERR;
+       }
+       num_old_entries++;
+
+       ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_new_entries, &dos_name);
+       if (ret) {
+               buf_unlock(sb, sector_old);
+               return ret;
+       }
+
+       if (num_old_entries < num_new_entries) {
+               newentry = find_empty_entry(inode, p_dir, num_new_entries);
+               if (newentry < 0) {
+                       buf_unlock(sb, sector_old);
+                       return FFS_FULL;
+               }
+
+               epnew = get_entry_in_dir(sb, p_dir, newentry, &sector_new);
+               if (!epnew) {
+                       buf_unlock(sb, sector_old);
+                       return FFS_MEDIAERR;
+               }
+
+               memcpy((void *) epnew, (void *) epold, DENTRY_SIZE);
+               if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) {
+                       p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE);
+                       fid->attr |= ATTR_ARCHIVE;
+               }
+               buf_modify(sb, sector_new);
+               buf_unlock(sb, sector_old);
+
+               if (p_fs->vol_type == EXFAT) {
+                       epold = get_entry_in_dir(sb, p_dir, oldentry+1, &sector_old);
+                       buf_lock(sb, sector_old);
+                       epnew = get_entry_in_dir(sb, p_dir, newentry+1, &sector_new);
+
+                       if (!epold || !epnew) {
+                               buf_unlock(sb, sector_old);
+                               return FFS_MEDIAERR;
+                       }
+
+                       memcpy((void *) epnew, (void *) epold, DENTRY_SIZE);
+                       buf_modify(sb, sector_new);
+                       buf_unlock(sb, sector_old);
+               }
+
+               ret = p_fs->fs_func->init_ext_entry(sb, p_dir, newentry, num_new_entries, p_uniname, &dos_name);
+               if (ret != FFS_SUCCESS)
+                       return ret;
+
+               p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, 0, num_old_entries);
+               fid->entry = newentry;
+       } else {
+               if (p_fs->fs_func->get_entry_type(epold) == TYPE_FILE) {
+                       p_fs->fs_func->set_entry_attr(epold, p_fs->fs_func->get_entry_attr(epold) | ATTR_ARCHIVE);
+                       fid->attr |= ATTR_ARCHIVE;
+               }
+               buf_modify(sb, sector_old);
+               buf_unlock(sb, sector_old);
+
+               ret = p_fs->fs_func->init_ext_entry(sb, p_dir, oldentry, num_new_entries, p_uniname, &dos_name);
+               if (ret != FFS_SUCCESS)
+                       return ret;
+
+               p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, num_new_entries, num_old_entries);
+       }
+
+       return FFS_SUCCESS;
+} /* end of rename_file */
+
+s32 move_file(struct inode *inode, CHAIN_T *p_olddir, s32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid)
+{
+       s32 ret, newentry, num_new_entries, num_old_entries;
+       sector_t sector_mov, sector_new;
+       CHAIN_T clu;
+       DOS_NAME_T dos_name;
+       DENTRY_T *epmov, *epnew;
+       struct super_block *sb = inode->i_sb;
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       epmov = get_entry_in_dir(sb, p_olddir, oldentry, &sector_mov);
+       if (!epmov)
+               return FFS_MEDIAERR;
+
+       /* check if the source and target directory is the same */
+       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);
+
+       /* buf_lock() before call count_ext_entries() */
+       num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_olddir, oldentry, epmov);
+       if (num_old_entries < 0) {
+               buf_unlock(sb, sector_mov);
+               return FFS_MEDIAERR;
+       }
+       num_old_entries++;
+
+       ret = get_num_entries_and_dos_name(sb, p_newdir, p_uniname, &num_new_entries, &dos_name);
+       if (ret) {
+               buf_unlock(sb, sector_mov);
+               return ret;
+       }
+
+       newentry = find_empty_entry(inode, p_newdir, num_new_entries);
+       if (newentry < 0) {
+               buf_unlock(sb, sector_mov);
+               return FFS_FULL;
+       }
+
+       epnew = get_entry_in_dir(sb, p_newdir, newentry, &sector_new);
+       if (!epnew) {
+               buf_unlock(sb, sector_mov);
+               return FFS_MEDIAERR;
+       }
+
+       memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE);
+       if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) {
+               p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE);
+               fid->attr |= ATTR_ARCHIVE;
+       }
+       buf_modify(sb, sector_new);
+       buf_unlock(sb, sector_mov);
+
+       if (p_fs->vol_type == EXFAT) {
+               epmov = get_entry_in_dir(sb, p_olddir, oldentry+1, &sector_mov);
+               buf_lock(sb, sector_mov);
+               epnew = get_entry_in_dir(sb, p_newdir, newentry+1, &sector_new);
+               if (!epmov || !epnew) {
+                       buf_unlock(sb, sector_mov);
+                       return FFS_MEDIAERR;
+               }
+
+               memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE);
+               buf_modify(sb, sector_new);
+               buf_unlock(sb, sector_mov);
+       } else if (p_fs->fs_func->get_entry_type(epnew) == TYPE_DIR) {
+               /* change ".." pointer to new parent dir */
+               clu.dir = p_fs->fs_func->get_entry_clu0(epnew);
+               clu.flags = 0x01;
+
+               epnew = get_entry_in_dir(sb, &clu, 1, &sector_new);
+               if (!epnew)
+                       return FFS_MEDIAERR;
+
+               if (p_newdir->dir == p_fs->root_dir)
+                       p_fs->fs_func->set_entry_clu0(epnew, CLUSTER_32(0));
+               else
+                       p_fs->fs_func->set_entry_clu0(epnew, p_newdir->dir);
+               buf_modify(sb, sector_new);
+       }
+
+       ret = p_fs->fs_func->init_ext_entry(sb, p_newdir, newentry, num_new_entries, p_uniname, &dos_name);
+       if (ret != FFS_SUCCESS)
+               return ret;
+
+       p_fs->fs_func->delete_dir_entry(sb, p_olddir, oldentry, 0, num_old_entries);
+
+       fid->dir.dir = p_newdir->dir;
+       fid->dir.size = p_newdir->size;
+       fid->dir.flags = p_newdir->flags;
+
+       fid->entry = newentry;
+
+       return FFS_SUCCESS;
+} /* end of move_file */
+
+/*
+ *  Sector Read/Write Functions
+ */
+
+s32 sector_read(struct super_block *sb, sector_t sec, struct buffer_head **bh, s32 read)
+{
+       s32 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)) {
+               printk("[EXFAT] sector_read: out of range error! (sec = %llu)\n", (unsigned long long)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;
+} /* end of sector_read */
+
+s32 sector_write(struct super_block *sb, sector_t sec, struct buffer_head *bh, s32 sync)
+{
+       s32 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)) {
+               printk("[EXFAT] sector_write: out of range error! (sec = %llu)\n", (unsigned long long)sec);
+               fs_error(sb);
+               return ret;
+       }
+
+       if (bh == NULL) {
+               printk("[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;
+} /* end of sector_write */
+
+s32 multi_sector_read(struct super_block *sb, sector_t sec, struct buffer_head **bh, s32 num_secs, s32 read)
+{
+       s32 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)) {
+               printk("[EXFAT] multi_sector_read: out of range error! (sec = %llu, num_secs = %d)\n",
+                      (unsigned long long)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;
+} /* end of multi_sector_read */
+
+s32 multi_sector_write(struct super_block *sb, sector_t sec, struct buffer_head *bh, s32 num_secs, s32 sync)
+{
+       s32 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)) {
+               printk("[EXFAT] multi_sector_write: out of range error! (sec = %llu, num_secs = %d)\n",
+                      (unsigned long long)sec, num_secs);
+               fs_error(sb);
+               return ret;
+       }
+       if (bh == NULL) {
+               printk("[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;
+} /* end of multi_sector_write */
diff --git a/fs/exfat/exfat_core.h b/fs/exfat/exfat_core.h
new file mode 100644 (file)
index 0000000..52d05c7
--- /dev/null
@@ -0,0 +1,671 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_core.h                                              */
+/*  PURPOSE : Header File for exFAT File Manager                        */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#ifndef _EXFAT_H
+#define _EXFAT_H
+
+#include "exfat_config.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 CONFIG_EXFAT_KERNEL_DEBUG
+  /* For Debugging Purpose */
+       /* IOCTL code 'f' used by
+        *   - file systems typically #0~0x1F
+        *   - embedded terminal devices #128~
+        *   - exts for debugging purpose #99
+        * number 100 and 101 is availble now but has possible conflicts
+        */
+#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 /* CONFIG_EXFAT_KERNEL_DEBUG */
+
+       /*----------------------------------------------------------------------*/
+       /*  Constant & Macro Definitions                                        */
+       /*----------------------------------------------------------------------*/
+
+#define DENTRY_SIZE             32          /* dir entry size */
+#define DENTRY_SIZE_BITS        5
+
+/* PBR entries */
+#define PBR_SIGNATURE           0xAA55
+#define EXT_SIGNATURE           0xAA550000
+#define VOL_LABEL               "NO NAME    " /* size should be 11 */
+#define OEM_NAME                "MSWIN4.1"  /* size should be 8 */
+#define STR_FAT12               "FAT12   "  /* size should be 8 */
+#define STR_FAT16               "FAT16   "  /* size should be 8 */
+#define STR_FAT32               "FAT32   "  /* size should be 8 */
+#define STR_EXFAT               "EXFAT   "  /* size should be 8 */
+#define VOL_CLEAN               0x0000
+#define VOL_DIRTY               0x0002
+
+/* max number of clusters */
+#define FAT12_THRESHOLD         4087        /* 2^12 - 1 + 2 (clu 0 & 1) */
+#define FAT16_THRESHOLD         65527       /* 2^16 - 1 + 2 */
+#define FAT32_THRESHOLD         268435457   /* 2^28 - 1 + 2 */
+#define EXFAT_THRESHOLD         268435457   /* 2^28 - 1 + 2 */
+
+/* file types */
+#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
+
+/* time modes */
+#define TM_CREATE               0
+#define TM_MODIFY               1
+#define TM_ACCESS               2
+
+/* checksum types */
+#define CS_DIR_ENTRY            0
+#define CS_PBR_SECTOR           1
+#define CS_DEFAULT              2
+
+#define CLUSTER_16(x)           ((u16)(x))
+#define CLUSTER_32(x)           ((u32)(x))
+
+#define FALSE                  0
+#define TRUE                   1
+
+#define MIN(a, b)              (((a) < (b)) ? (a) : (b))
+#define MAX(a, b)              (((a) > (b)) ? (a) : (b))
+
+#define START_SECTOR(x) \
+       ((((sector_t)((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)                   \
+               ((u32)((((sec) - p_fs->data_start_sector) >> p_fs->sectors_per_clu_bits) + 2))
+
+#define GET16(p_src) \
+       (((u16)(p_src)[0]) | (((u16)(p_src)[1]) << 8))
+#define GET32(p_src) \
+       (((u32)(p_src)[0]) | (((u32)(p_src)[1]) << 8) | \
+       (((u32)(p_src)[2]) << 16) | (((u32)(p_src)[3]) << 24))
+#define GET64(p_src) \
+       (((u64)(p_src)[0]) | (((u64)(p_src)[1]) << 8) | \
+       (((u64)(p_src)[2]) << 16) | (((u64)(p_src)[3]) << 24) | \
+       (((u64)(p_src)[4]) << 32) | (((u64)(p_src)[5]) << 40) | \
+       (((u64)(p_src)[6]) << 48) | (((u64)(p_src)[7]) << 56))
+
+
+#define SET16(p_dst, src)                                  \
+       do {                                              \
+               (p_dst)[0] = (u8)(src);                     \
+               (p_dst)[1] = (u8)(((u16)(src)) >> 8);       \
+       } while (0)
+#define SET32(p_dst, src)                                  \
+       do {                                              \
+               (p_dst)[0] = (u8)(src);                     \
+               (p_dst)[1] = (u8)(((u32)(src)) >> 8);       \
+               (p_dst)[2] = (u8)(((u32)(src)) >> 16);      \
+               (p_dst)[3] = (u8)(((u32)(src)) >> 24);      \
+       } while (0)
+#define SET64(p_dst, src)                                  \
+       do {                                              \
+               (p_dst)[0] = (u8)(src);                   \
+               (p_dst)[1] = (u8)(((u64)(src)) >> 8);     \
+               (p_dst)[2] = (u8)(((u64)(src)) >> 16);    \
+               (p_dst)[3] = (u8)(((u64)(src)) >> 24);    \
+               (p_dst)[4] = (u8)(((u64)(src)) >> 32);    \
+               (p_dst)[5] = (u8)(((u64)(src)) >> 40);    \
+               (p_dst)[6] = (u8)(((u64)(src)) >> 48);    \
+               (p_dst)[7] = (u8)(((u64)(src)) >> 56);    \
+       } while (0)
+
+#ifdef __LITTLE_ENDIAN
+#define GET16_A(p_src)         (*((u16 *)(p_src)))
+#define GET32_A(p_src)         (*((u32 *)(p_src)))
+#define GET64_A(p_src)         (*((u64 *)(p_src)))
+#define SET16_A(p_dst, src)    (*((u16 *)(p_dst)) = (u16)(src))
+#define SET32_A(p_dst, src)    (*((u32 *)(p_dst)) = (u32)(src))
+#define SET64_A(p_dst, src)    (*((u64 *)(p_dst)) = (u64)(src))
+#else /* BIG_ENDIAN */
+#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
+
+/* Upcase tabel mecro */
+#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)
+
+#if CONFIG_EXFAT_DEBUG_MSG
+#define DPRINTK(...)                   \
+       do {                                                            \
+               printk("[EXFAT] " __VA_ARGS__); \
+       } while (0)
+#else
+#define DPRINTK(...)
+#endif
+
+static inline u16 get_col_index(u16 i)
+{
+       return i >> LOW_INDEX_BIT;
+}
+static inline u16 get_row_index(u16 i)
+{
+       return i & ~HIGH_INDEX_MASK;
+}
+/*----------------------------------------------------------------------*/
+/*  Type Definitions                                                    */
+/*----------------------------------------------------------------------*/
+
+/* MS_DOS FAT partition boot record (512 bytes) */
+typedef struct {
+       u8       jmp_boot[3];
+       u8       oem_name[8];
+       u8       bpb[109];
+       u8       boot_code[390];
+       u8       signature[2];
+} PBR_SECTOR_T;
+
+/* MS-DOS FAT12/16 BIOS parameter block (51 bytes) */
+typedef struct {
+       u8       sector_size[2];
+       u8       sectors_per_clu;
+       u8       num_reserved[2];
+       u8       num_fats;
+       u8       num_root_entries[2];
+       u8       num_sectors[2];
+       u8       media_type;
+       u8       num_fat_sectors[2];
+       u8       sectors_in_track[2];
+       u8       num_heads[2];
+       u8       num_hid_sectors[4];
+       u8       num_huge_sectors[4];
+
+       u8       phy_drv_no;
+       u8       reserved;
+       u8       ext_signature;
+       u8       vol_serial[4];
+       u8       vol_label[11];
+       u8       vol_type[8];
+} BPB16_T;
+
+/* MS-DOS FAT32 BIOS parameter block (79 bytes) */
+typedef struct {
+       u8       sector_size[2];
+       u8       sectors_per_clu;
+       u8       num_reserved[2];
+       u8       num_fats;
+       u8       num_root_entries[2];
+       u8       num_sectors[2];
+       u8       media_type;
+       u8       num_fat_sectors[2];
+       u8       sectors_in_track[2];
+       u8       num_heads[2];
+       u8       num_hid_sectors[4];
+       u8       num_huge_sectors[4];
+       u8       num_fat32_sectors[4];
+       u8       ext_flags[2];
+       u8       fs_version[2];
+       u8       root_cluster[4];
+       u8       fsinfo_sector[2];
+       u8       backup_sector[2];
+       u8       reserved[12];
+
+       u8       phy_drv_no;
+       u8       ext_reserved;
+       u8       ext_signature;
+       u8       vol_serial[4];
+       u8       vol_label[11];
+       u8       vol_type[8];
+} BPB32_T;
+
+/* MS-DOS EXFAT BIOS parameter block (109 bytes) */
+typedef struct {
+       u8       reserved1[53];
+       u8       vol_offset[8];
+       u8       vol_length[8];
+       u8       fat_offset[4];
+       u8       fat_length[4];
+       u8       clu_offset[4];
+       u8       clu_count[4];
+       u8       root_cluster[4];
+       u8       vol_serial[4];
+       u8       fs_version[2];
+       u8       vol_flags[2];
+       u8       sector_size_bits;
+       u8       sectors_per_clu_bits;
+       u8       num_fats;
+       u8       phy_drv_no;
+       u8       perc_in_use;
+       u8       reserved2[7];
+} BPBEX_T;
+
+/* MS-DOS FAT file system information sector (512 bytes) */
+typedef struct {
+       u8       signature1[4];
+       u8       reserved1[480];
+       u8       signature2[4];
+       u8       free_cluster[4];
+       u8       next_cluster[4];
+       u8       reserved2[14];
+       u8       signature3[2];
+} FSI_SECTOR_T;
+
+/* MS-DOS FAT directory entry (32 bytes) */
+typedef struct {
+       u8       dummy[32];
+} DENTRY_T;
+
+typedef struct {
+       u8       name[DOS_NAME_LENGTH];
+       u8       attr;
+       u8       lcase;
+       u8       create_time_ms;
+       u8       create_time[2];
+       u8       create_date[2];
+       u8       access_date[2];
+       u8       start_clu_hi[2];
+       u8       modify_time[2];
+       u8       modify_date[2];
+       u8       start_clu_lo[2];
+       u8       size[4];
+} DOS_DENTRY_T;
+
+/* MS-DOS FAT extended directory entry (32 bytes) */
+typedef struct {
+       u8       order;
+       u8       unicode_0_4[10];
+       u8       attr;
+       u8       sysid;
+       u8       checksum;
+       u8       unicode_5_10[12];
+       u8       start_clu[2];
+       u8       unicode_11_12[4];
+} EXT_DENTRY_T;
+
+/* MS-DOS EXFAT file directory entry (32 bytes) */
+typedef struct {
+       u8       type;
+       u8       num_ext;
+       u8       checksum[2];
+       u8       attr[2];
+       u8       reserved1[2];
+       u8       create_time[2];
+       u8       create_date[2];
+       u8       modify_time[2];
+       u8       modify_date[2];
+       u8       access_time[2];
+       u8       access_date[2];
+       u8       create_time_ms;
+       u8       modify_time_ms;
+       u8       access_time_ms;
+       u8       reserved2[9];
+} FILE_DENTRY_T;
+
+/* MS-DOS EXFAT stream extension directory entry (32 bytes) */
+typedef struct {
+       u8       type;
+       u8       flags;
+       u8       reserved1;
+       u8       name_len;
+       u8       name_hash[2];
+       u8       reserved2[2];
+       u8       valid_size[8];
+       u8       reserved3[4];
+       u8       start_clu[4];
+       u8       size[8];
+} STRM_DENTRY_T;
+
+/* MS-DOS EXFAT file name directory entry (32 bytes) */
+typedef struct {
+       u8       type;
+       u8       flags;
+       u8       unicode_0_14[30];
+} NAME_DENTRY_T;
+
+/* MS-DOS EXFAT allocation bitmap directory entry (32 bytes) */
+typedef struct {
+       u8       type;
+       u8       flags;
+       u8       reserved[18];
+       u8       start_clu[4];
+       u8       size[8];
+} BMAP_DENTRY_T;
+
+/* MS-DOS EXFAT up-case table directory entry (32 bytes) */
+typedef struct {
+       u8       type;
+       u8       reserved1[3];
+       u8       checksum[4];
+       u8       reserved2[12];
+       u8       start_clu[4];
+       u8       size[8];
+} CASE_DENTRY_T;
+
+/* MS-DOS EXFAT volume label directory entry (32 bytes) */
+typedef struct {
+       u8       type;
+       u8       label_len;
+       u8       unicode_0_10[22];
+       u8       reserved[8];
+} VOLM_DENTRY_T;
+
+/* unused entry hint information */
+typedef struct {
+       u32      dir;
+       s32       entry;
+       CHAIN_T     clu;
+} UENTRY_T;
+
+typedef struct {
+       s32       (*alloc_cluster)(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain);
+       void        (*free_cluster)(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse);
+       s32       (*count_used_clusters)(struct super_block *sb);
+
+       s32      (*init_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type,
+                                                                u32 start_clu, u64 size);
+       s32      (*init_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries,
+                                                                UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname);
+       s32       (*find_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type);
+       void        (*delete_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 offset, s32 num_entries);
+       void        (*get_uni_name_from_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname);
+       s32       (*count_ext_entries)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry);
+       s32       (*calc_num_entries)(UNI_NAME_T *p_uniname);
+
+       u32      (*get_entry_type)(DENTRY_T *p_entry);
+       void        (*set_entry_type)(DENTRY_T *p_entry, u32 type);
+       u32      (*get_entry_attr)(DENTRY_T *p_entry);
+       void        (*set_entry_attr)(DENTRY_T *p_entry, u32 attr);
+       u8       (*get_entry_flag)(DENTRY_T *p_entry);
+       void        (*set_entry_flag)(DENTRY_T *p_entry, u8 flag);
+       u32      (*get_entry_clu0)(DENTRY_T *p_entry);
+       void        (*set_entry_clu0)(DENTRY_T *p_entry, u32 clu0);
+       u64      (*get_entry_size)(DENTRY_T *p_entry);
+       void        (*set_entry_size)(DENTRY_T *p_entry, u64 size);
+       void        (*get_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode);
+       void        (*set_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode);
+} FS_FUNC_T;
+
+typedef struct __FS_INFO_T {
+       u32      drv;                    /* drive ID */
+       u32      vol_type;               /* volume FAT type */
+       u32      vol_id;                 /* volume serial number */
+
+       u64      num_sectors;            /* num of sectors in volume */
+       u32      num_clusters;           /* num of clusters in volume */
+       u32      cluster_size;           /* cluster size in bytes */
+       u32      cluster_size_bits;
+       u32      sectors_per_clu;        /* cluster size in sectors */
+       u32      sectors_per_clu_bits;
+
+       u32      PBR_sector;             /* PBR sector */
+       u32      FAT1_start_sector;      /* FAT1 start sector */
+       u32      FAT2_start_sector;      /* FAT2 start sector */
+       u32      root_start_sector;      /* root dir start sector */
+       u32      data_start_sector;      /* data area start sector */
+       u32      num_FAT_sectors;        /* num of FAT sectors */
+
+       u32      root_dir;               /* root dir cluster */
+       u32      dentries_in_root;       /* num of dentries in root dir */
+       u32      dentries_per_clu;       /* num of dentries per cluster */
+
+       u32      vol_flag;               /* volume dirty flag */
+       struct buffer_head *pbr_bh;         /* PBR sector */
+
+       u32      map_clu;                /* allocation bitmap start cluster */
+       u32      map_sectors;            /* num of allocation bitmap sectors */
+       struct buffer_head **vol_amap;      /* allocation bitmap */
+
+       u16      **vol_utbl;               /* upcase table */
+
+       u32      clu_srch_ptr;           /* cluster search pointer */
+       u32      used_clusters;          /* number of used clusters */
+       UENTRY_T    hint_uentry;         /* unused entry hint information */
+
+       u32      dev_ejected;            /* block device operation error flag */
+
+       FS_FUNC_T       *fs_func;
+       struct semaphore v_sem;
+
+       /* FAT cache */
+       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 */
+       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 {
+       sector_t        sector; /* sector number that contains file_entry */
+       s32     offset;         /* byte offset in the sector */
+       s32     alloc_flag;     /* flag in stream entry. 01 for cluster chain, 03 for contig. clusteres. */
+       u32 num_entries;
+
+       /* __buf should be the last member */
+       void *__buf;
+} ENTRY_SET_CACHE_T;
+
+/*----------------------------------------------------------------------*/
+/*  External Function Declarations                                      */
+/*----------------------------------------------------------------------*/
+
+/* file system initialization & shutdown functions */
+s32 ffsInit(void);
+s32 ffsShutdown(void);
+
+/* volume management functions */
+s32 ffsMountVol(struct super_block *sb);
+s32 ffsUmountVol(struct super_block *sb);
+s32 ffsCheckVol(struct super_block *sb);
+s32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info);
+s32 ffsSyncVol(struct super_block *sb, s32 do_sync);
+
+/* file management functions */
+s32 ffsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid);
+s32 ffsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid);
+s32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount);
+s32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount);
+s32 ffsTruncateFile(struct inode *inode, u64 old_size, u64 new_size);
+s32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry);
+s32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid);
+s32 ffsSetAttr(struct inode *inode, u32 attr);
+s32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info);
+s32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info);
+s32 ffsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu);
+
+/* directory management functions */
+s32 ffsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid);
+s32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_ent);
+s32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid);
+
+/*----------------------------------------------------------------------*/
+/*  External Function Declarations (NOT TO UPPER LAYER)                 */
+/*----------------------------------------------------------------------*/
+
+/* fs management functions */
+s32  fs_init(void);
+s32  fs_shutdown(void);
+void   fs_set_vol_flags(struct super_block *sb, u32 new_flag);
+void   fs_sync(struct super_block *sb, s32 do_sync);
+void   fs_error(struct super_block *sb);
+
+/* cluster management functions */
+s32   clear_cluster(struct super_block *sb, u32 clu);
+s32  fat_alloc_cluster(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain);
+s32  exfat_alloc_cluster(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain);
+void   fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse);
+void   exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse);
+u32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain);
+s32  count_num_clusters(struct super_block *sb, CHAIN_T *dir);
+s32  fat_count_used_clusters(struct super_block *sb);
+s32  exfat_count_used_clusters(struct super_block *sb);
+void   exfat_chain_cont_cluster(struct super_block *sb, u32 chain, s32 len);
+
+/* allocation bitmap management functions */
+s32  load_alloc_bitmap(struct super_block *sb);
+void   free_alloc_bitmap(struct super_block *sb);
+s32   set_alloc_bitmap(struct super_block *sb, u32 clu);
+s32   clr_alloc_bitmap(struct super_block *sb, u32 clu);
+u32 test_alloc_bitmap(struct super_block *sb, u32 clu);
+void   sync_alloc_bitmap(struct super_block *sb);
+
+/* upcase table management functions */
+s32  load_upcase_table(struct super_block *sb);
+void   free_upcase_table(struct super_block *sb);
+
+/* dir entry management functions */
+u32 fat_get_entry_type(DENTRY_T *p_entry);
+u32 exfat_get_entry_type(DENTRY_T *p_entry);
+void   fat_set_entry_type(DENTRY_T *p_entry, u32 type);
+void   exfat_set_entry_type(DENTRY_T *p_entry, u32 type);
+u32 fat_get_entry_attr(DENTRY_T *p_entry);
+u32 exfat_get_entry_attr(DENTRY_T *p_entry);
+void   fat_set_entry_attr(DENTRY_T *p_entry, u32 attr);
+void   exfat_set_entry_attr(DENTRY_T *p_entry, u32 attr);
+u8  fat_get_entry_flag(DENTRY_T *p_entry);
+u8  exfat_get_entry_flag(DENTRY_T *p_entry);
+void   fat_set_entry_flag(DENTRY_T *p_entry, u8 flag);
+void   exfat_set_entry_flag(DENTRY_T *p_entry, u8 flag);
+u32 fat_get_entry_clu0(DENTRY_T *p_entry);
+u32 exfat_get_entry_clu0(DENTRY_T *p_entry);
+void   fat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu);
+void   exfat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu);
+u64 fat_get_entry_size(DENTRY_T *p_entry);
+u64 exfat_get_entry_size(DENTRY_T *p_entry);
+void   fat_set_entry_size(DENTRY_T *p_entry, u64 size);
+void   exfat_set_entry_size(DENTRY_T *p_entry, u64 size);
+void   fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode);
+void   exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode);
+void   fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode);
+void   exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode);
+s32   fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, u32 start_clu, u64 size);
+s32   exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, u32 start_clu, u64 size);
+s32   fat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname);
+s32   exfat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname);
+void   init_dos_entry(DOS_DENTRY_T *ep, u32 type, u32 start_clu);
+void   init_ext_entry(EXT_DENTRY_T *ep, s32 order, u8 chksum, u16 *uniname);
+void   init_file_entry(FILE_DENTRY_T *ep, u32 type);
+void   init_strm_entry(STRM_DENTRY_T *ep, u8 flags, u32 start_clu, u64 size);
+void   init_name_entry(NAME_DENTRY_T *ep, u16 *uniname);
+void   fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries);
+void   exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries);
+
+s32   find_location(struct super_block *sb, CHAIN_T *p_dir, s32 entry, sector_t *sector, s32 *offset);
+DENTRY_T *get_entry_with_sector(struct super_block *sb, sector_t sector, s32 offset);
+DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, sector_t *sector);
+ENTRY_SET_CACHE_T *get_entry_set_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, DENTRY_T **file_ep);
+void release_entry_set(ENTRY_SET_CACHE_T *es);
+s32 write_whole_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es);
+s32 write_partial_entries_in_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, u32 count);
+s32  search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, s32 num_entries);
+s32  find_empty_entry(struct inode *inode, CHAIN_T *p_dir, s32 num_entries);
+s32  fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type);
+s32  exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type);
+s32  fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry);
+s32  exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry);
+s32  count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, u32 type);
+void   update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, s32 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);
+
+/* name conversion functions */
+s32  get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 *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, u8 mode);
+void   fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname);
+void   exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname);
+s32  extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, u16 *uniname, s32 order);
+s32  extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, u16 *uniname, s32 order);
+s32  fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname);
+void   fat_attach_count_to_dos_name(u8 *dosname, s32 count);
+s32  fat_calc_num_entries(UNI_NAME_T *p_uniname);
+s32  exfat_calc_num_entries(UNI_NAME_T *p_uniname);
+u8  calc_checksum_1byte(void *data, s32 len, u8 chksum);
+u16 calc_checksum_2byte(void *data, s32 len, u16 chksum, s32 type);
+u32 calc_checksum_4byte(void *data, s32 len, u32 chksum, s32 type);
+
+/* name resolution functions */
+s32  resolve_path(struct inode *inode, char *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname);
+s32  resolve_name(u8 *name, u8 **arg);
+
+/* file operation functions */
+s32  fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr);
+s32  fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr);
+s32  exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr);
+s32  create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid);
+s32  create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, u8 mode, FILE_ID_T *fid);
+void   remove_file(struct inode *inode, CHAIN_T *p_dir, s32 entry);
+s32  rename_file(struct inode *inode, CHAIN_T *p_dir, s32 old_entry, UNI_NAME_T *p_uniname, FILE_ID_T *fid);
+s32  move_file(struct inode *inode, CHAIN_T *p_olddir, s32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid);
+
+/* sector read/write functions */
+s32   sector_read(struct super_block *sb, sector_t sec, struct buffer_head **bh, s32 read);
+s32   sector_write(struct super_block *sb, sector_t sec, struct buffer_head *bh, s32 sync);
+s32   multi_sector_read(struct super_block *sb, sector_t sec, struct buffer_head **bh, s32 num_secs, s32 read);
+s32   multi_sector_write(struct super_block *sb, sector_t sec, struct buffer_head *bh, s32 num_secs, s32 sync);
+
+#endif /* _EXFAT_H */
diff --git a/fs/exfat/exfat_data.c b/fs/exfat/exfat_data.c
new file mode 100644 (file)
index 0000000..65da07a
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_data.c                                              */
+/*  PURPOSE : exFAT Configuable Data Definitions                        */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#include "exfat_config.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_core.h"
+
+/*======================================================================*/
+/*                                                                      */
+/*                    GLOBAL VARIABLE DEFINITIONS                       */
+/*                                                                      */
+/*======================================================================*/
+
+/*----------------------------------------------------------------------*/
+/*  File Manager                                                        */
+/*----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+/*  Buffer Manager                                                      */
+/*----------------------------------------------------------------------*/
+
+/* FAT cache */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
+DECLARE_MUTEX(f_sem);
+#else
+DEFINE_SEMAPHORE(f_sem);
+#endif
+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 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
+DECLARE_MUTEX(b_sem);
+#else
+DEFINE_SEMAPHORE(b_sem);
+#endif
+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/fs/exfat/exfat_data.h b/fs/exfat/exfat_data.h
new file mode 100644 (file)
index 0000000..53b0e39
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_data.h                                              */
+/*  PURPOSE : Header File for exFAT Configuable Constants               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#ifndef _EXFAT_DATA_H
+#define _EXFAT_DATA_H
+
+#include "exfat_config.h"
+
+/*======================================================================*/
+/*                                                                      */
+/*                        FFS CONFIGURATIONS                            */
+/*                  (CHANGE THIS PART IF REQUIRED)                      */
+/*                                                                      */
+/*======================================================================*/
+
+/* max number of root directory entries in FAT12/16 */
+/* (should be an exponential value of 2)            */
+#define MAX_DENTRY              512
+
+/* cache size (in number of sectors)                */
+/* (should be an exponential value of 2)            */
+#define FAT_CACHE_SIZE          128
+#define FAT_CACHE_HASH_SIZE     64
+#define BUF_CACHE_SIZE          256
+#define BUF_CACHE_HASH_SIZE     64
+
+#endif /* _EXFAT_DATA_H */
diff --git a/fs/exfat/exfat_nls.c b/fs/exfat/exfat_nls.c
new file mode 100644 (file)
index 0000000..a48b3d0
--- /dev/null
@@ -0,0 +1,448 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_nls.c                                               */
+/*  PURPOSE : exFAT NLS Manager                                         */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#include "exfat_config.h"
+#include "exfat_data.h"
+
+#include "exfat_nls.h"
+#include "exfat_api.h"
+#include "exfat_super.h"
+#include "exfat_core.h"
+
+#include <linux/nls.h>
+
+/*----------------------------------------------------------------------*/
+/*  Global Variable Definitions                                         */
+/*----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+/*  Local Variable Definitions                                          */
+/*----------------------------------------------------------------------*/
+
+static u16 bad_dos_chars[] = {
+       /* + , ; = [ ] */
+       0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D,
+       0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D,
+       0
+};
+
+static u16 bad_uni_chars[] = {
+       /* " * / : < > ? \ | */
+       0x0022,         0x002A, 0x002F, 0x003A,
+       0x003C, 0x003E, 0x003F, 0x005C, 0x007C,
+       0
+};
+
+/*----------------------------------------------------------------------*/
+/*  Local Function Declarations                                         */
+/*----------------------------------------------------------------------*/
+
+static s32  convert_uni_to_ch(struct nls_table *nls, u8 *ch, u16 uni, s32 *lossy);
+static s32  convert_ch_to_uni(struct nls_table *nls, u16 *uni, u8 *ch, s32 *lossy);
+
+/*======================================================================*/
+/*  Global Function Definitions                                         */
+/*======================================================================*/
+
+u16 nls_upper(struct super_block *sb, u16 a)
+{
+       FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+       if (EXFAT_SB(sb)->options.casesensitive)
+               return a;
+       if (p_fs->vol_utbl != NULL && (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;
+}
+
+u16 *nls_wstrchr(u16 *str, u16 wchar)
+{
+       while (*str) {
+               if (*(str++) == wchar)
+                       return str;
+       }
+
+       return 0;
+}
+
+s32 nls_dosname_cmp(struct super_block *sb, u8 *a, u8 *b)
+{
+       return strncmp((void *) a, (void *) b, DOS_NAME_LENGTH);
+} /* end of nls_dosname_cmp */
+
+s32 nls_uniname_cmp(struct super_block *sb, u16 *a, u16 *b)
+{
+       int 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;
+} /* end of nls_uniname_cmp */
+
+void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, s32 *p_lossy)
+{
+       int i, j, len, lossy = FALSE;
+       u8 buf[MAX_CHARSET_SIZE];
+       u8 lower = 0, upper = 0;
+       u8 *dosname = p_dosname->name;
+       u16 *uniname = p_uniname->name;
+       u16 *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, (u16 *) UNI_CUR_DIR_NAME)) {
+               *(dosname) = '.';
+               p_dosname->name_case = 0x0;
+               if (p_lossy != NULL)
+                       *p_lossy = FALSE;
+               return;
+       }
+
+       if (!nls_uniname_cmp(sb, uniname, (u16 *) UNI_PAR_DIR_NAME)) {
+               *(dosname) = '.';
+               *(dosname+1) = '.';
+               p_dosname->name_case = 0x0;
+               if (p_lossy != NULL)
+                       *p_lossy = FALSE;
+               return;
+       }
+
+       /* search for the last embedded period */
+       last_period = NULL;
+       for (p = uniname; *p; p++) {
+               if (*p == (u16) '.')
+                       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 == (u16) '\0') {
+                       break;
+               } else if (*uniname == (u16) ' ') {
+                       lossy = TRUE;
+               } else if (*uniname == (u16) '.') {
+                       if (uniname < last_period)
+                               lossy = TRUE;
+                       else
+                               i = 8;
+               } else if (nls_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 { /* len == 1 */
+                               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;
+} /* end of nls_uniname_to_dosname */
+
+void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname)
+{
+       int i = 0, j, n = 0;
+       u8 buf[DOS_NAME_LENGTH+2];
+       u8 *dosname = p_dosname->name;
+       u16 *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 = (u16) '\0';
+} /* end of nls_dosname_to_uniname */
+
+void nls_uniname_to_cstring(struct super_block *sb, u8 *p_cstring, UNI_NAME_T *p_uniname)
+{
+       int i, j, len;
+       u8 buf[MAX_CHARSET_SIZE];
+       u16 *uniname = p_uniname->name;
+       struct nls_table *nls = EXFAT_SB(sb)->nls_io;
+
+       if (nls == NULL) {
+               len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH, UTF16_HOST_ENDIAN, p_cstring, MAX_NAME_LENGTH);
+               p_cstring[len] = 0;
+               return;
+       }
+
+       i = 0;
+       while (i < (MAX_NAME_LENGTH-1)) {
+               if (*uniname == (u16) '\0')
+                       break;
+
+               len = convert_uni_to_ch(nls, buf, *uniname, NULL);
+
+               if (len > 1) {
+                       for (j = 0; j < len; j++)
+                               *p_cstring++ = (char) *(buf+j);
+               } else { /* len == 1 */
+                       *p_cstring++ = (char) *buf;
+               }
+
+               uniname++;
+               i++;
+       }
+
+       *p_cstring = '\0';
+} /* end of nls_uniname_to_cstring */
+
+void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 *p_lossy)
+{
+       int i, j, lossy = FALSE;
+       u8 *end_of_name;
+       u8 upname[MAX_NAME_LENGTH * 2];
+       u16 *uniname = p_uniname->name;
+       struct nls_table *nls = EXFAT_SB(sb)->nls_io;
+
+
+       /* strip all trailing spaces */
+       end_of_name = p_cstring + strlen((char *) p_cstring);
+
+       while (*(--end_of_name) == ' ') {
+               if (end_of_name < p_cstring)
+                       break;
+       }
+       *(++end_of_name) = '\0';
+
+       if (strcmp((char *) p_cstring, ".") && strcmp((char *) p_cstring, "..")) {
+
+               /* strip all trailing periods */
+               while (*(--end_of_name) == '.') {
+                       if (end_of_name < p_cstring)
+                               break;
+               }
+               *(++end_of_name) = '\0';
+       }
+
+       if (*p_cstring == '\0')
+               lossy = TRUE;
+
+       if (nls == NULL) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,101)
+               i = utf8s_to_utf16s(p_cstring, MAX_NAME_LENGTH, uniname);
+#else
+               i = utf8s_to_utf16s(p_cstring, MAX_NAME_LENGTH, UTF16_HOST_ENDIAN, uniname, MAX_NAME_LENGTH);
+#endif
+               for (j = 0; j < i; j++)
+                       SET16_A(upname + j * 2, nls_upper(sb, uniname[j]));
+               uniname[i] = '\0';
+       }
+       else {
+               i = j = 0;
+               while (j < (MAX_NAME_LENGTH-1)) {
+                       if (*(p_cstring+i) == '\0')
+                               break;
+
+                       i += convert_ch_to_uni(nls, uniname, (u8 *)(p_cstring+i), &lossy);
+
+                       if ((*uniname < 0x0020) || nls_wstrchr(bad_uni_chars, *uniname))
+                               lossy = TRUE;
+
+                       SET16_A(upname + j * 2, nls_upper(sb, *uniname));
+
+                       uniname++;
+                       j++;
+               }
+
+               if (*(p_cstring+i) != '\0')
+                       lossy = TRUE;
+               *uniname = (u16) '\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;
+} /* end of nls_cstring_to_uniname */
+
+/*======================================================================*/
+/*  Local Function Definitions                                          */
+/*======================================================================*/
+
+static s32 convert_ch_to_uni(struct nls_table *nls, u16 *uni, u8 *ch, s32 *lossy)
+{
+       int len;
+
+       *uni = 0x0;
+
+       if (ch[0] < 0x80) {
+               *uni = (u16) ch[0];
+               return 1;
+       }
+
+       len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni);
+       if (len < 0) {
+               /* conversion failed */
+               printk("%s: fail to use nls\n", __func__);
+               if (lossy != NULL)
+                       *lossy = TRUE;
+               *uni = (u16) '_';
+               if (!strcmp(nls->charset, "utf8"))
+                       return 1;
+               else
+                       return 2;
+       }
+
+       return len;
+} /* end of convert_ch_to_uni */
+
+static s32 convert_uni_to_ch(struct nls_table *nls, u8 *ch, u16 uni, s32 *lossy)
+{
+       int len;
+
+       ch[0] = 0x0;
+
+       if (uni < 0x0080) {
+               ch[0] = (u8) uni;
+               return 1;
+       }
+
+       len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE);
+       if (len < 0) {
+               /* conversion failed */
+               printk("%s: fail to use nls\n", __func__);
+               if (lossy != NULL)
+                       *lossy = TRUE;
+               ch[0] = '_';
+               return 1;
+       }
+
+       return len;
+
+} /* end of convert_uni_to_ch */
diff --git a/fs/exfat/exfat_nls.h b/fs/exfat/exfat_nls.h
new file mode 100644 (file)
index 0000000..bc516d7
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_nls.h                                               */
+/*  PURPOSE : Header File for exFAT NLS Manager                         */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#ifndef _EXFAT_NLS_H
+#define _EXFAT_NLS_H
+
+#include <linux/types.h>
+#include <linux/nls.h>
+
+#include "exfat_config.h"
+#include "exfat_api.h"
+
+/*----------------------------------------------------------------------*/
+/*  Constant & Macro Definitions                                        */
+/*----------------------------------------------------------------------*/
+
+#define NUM_UPCASE              2918
+
+#define DOS_CUR_DIR_NAME        ".          "
+#define DOS_PAR_DIR_NAME        "..         "
+
+#ifdef __LITTLE_ENDIAN
+#define UNI_CUR_DIR_NAME        ".\0"
+#define UNI_PAR_DIR_NAME        ".\0.\0"
+#else
+#define UNI_CUR_DIR_NAME        "\0."
+#define UNI_PAR_DIR_NAME        "\0.\0."
+#endif
+
+/*----------------------------------------------------------------------*/
+/*  Type Definitions                                                    */
+/*----------------------------------------------------------------------*/
+
+/* DOS name stucture */
+typedef struct {
+       u8       name[DOS_NAME_LENGTH];
+       u8       name_case;
+} DOS_NAME_T;
+
+/* unicode name stucture */
+typedef struct {
+       u16      name[MAX_NAME_LENGTH];
+       u16      name_hash;
+       u8       name_len;
+} UNI_NAME_T;
+
+/*----------------------------------------------------------------------*/
+/*  External Function Declarations                                      */
+/*----------------------------------------------------------------------*/
+
+/* NLS management function */
+u16 nls_upper(struct super_block *sb, u16 a);
+s32  nls_dosname_cmp(struct super_block *sb, u8 *a, u8 *b);
+s32  nls_uniname_cmp(struct super_block *sb, u16 *a, u16 *b);
+void   nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, s32 *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, u8 *p_cstring, UNI_NAME_T *p_uniname);
+void   nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 *p_lossy);
+
+#endif /* _EXFAT_NLS_H */
diff --git a/fs/exfat/exfat_oal.c b/fs/exfat/exfat_oal.c
new file mode 100644 (file)
index 0000000..7435442
--- /dev/null
@@ -0,0 +1,196 @@
+/* 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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_oal.c                                               */
+/*  PURPOSE : exFAT OS Adaptation Layer                                 */
+/*            (Semaphore Functions & Real-Time Clock Functions)         */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#include <linux/semaphore.h>
+#include <linux/time.h>
+
+#include "exfat_config.h"
+#include "exfat_api.h"
+#include "exfat_oal.h"
+
+/*======================================================================*/
+/*                                                                      */
+/*            SEMAPHORE FUNCTIONS                                       */
+/*                                                                      */
+/*======================================================================*/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
+DECLARE_MUTEX(z_sem);
+#else
+DEFINE_SEMAPHORE(z_sem);
+#endif
+
+s32 sm_init(struct semaphore *sm)
+{
+       sema_init(sm, 1);
+       return 0;
+} /* end of sm_init */
+
+s32 sm_P(struct semaphore *sm)
+{
+       down(sm);
+       return 0;
+} /* end of sm_P */
+
+void sm_V(struct semaphore *sm)
+{
+       up(sm);
+} /* end of sm_V */
+
+
+/*======================================================================*/
+/*                                                                      */
+/*            REAL-TIME CLOCK FUNCTIONS                                 */
+/*                                                                      */
+/*======================================================================*/
+
+extern struct timezone sys_tz;
+
+/*
+ * The epoch of FAT timestamp is 1980.
+ *     :  bits  : value
+ * date:  0 -  4: day    (1 -  31)
+ * date:  5 -  8: month  (1 -  12)
+ * date:  9 - 15: year   (0 - 127) from 1980
+ * time:  0 -  4: sec    (0 -  29) 2sec counts
+ * time:  5 - 10: min    (0 -  59)
+ * time: 11 - 15: hour   (0 -  23)
+ */
+#define UNIX_SECS_1980   315532800L
+
+#if BITS_PER_LONG == 64
+#define UNIX_SECS_2108   4354819200L
+#endif
+/* days between 1.1.70 and 1.1.80 (2 leap days) */
+#define DAYS_DELTA_DECADE      (365 * 10 + 2)
+/* 120 (2100 - 1980) isn't leap year */
+#define NO_LEAP_YEAR_2100    (120)
+#define IS_LEAP_YEAR(y)  (!((y) & 3) && (y) != NO_LEAP_YEAR_2100)
+
+#define SECS_PER_MIN     (60)
+#define SECS_PER_HOUR    (60 * SECS_PER_MIN)
+#define SECS_PER_DAY     (24 * SECS_PER_HOUR)
+
+#define MAKE_LEAP_YEAR(leap_year, year)                         \
+       do {                                                    \
+               if (unlikely(year > NO_LEAP_YEAR_2100))         \
+                       leap_year = ((year + 3) / 4) - 1;       \
+               else                                            \
+                       leap_year = ((year + 3) / 4);           \
+       } while (0)
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+static time_t accum_days_in_year[] = {
+       /* Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec */
+       0,   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0,
+};
+
+TIMESTAMP_T *tm_current(TIMESTAMP_T *tp)
+{
+       struct timespec ts;
+       time_t second, day, leap_day, month, year;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0)
+       ts = CURRENT_TIME_SEC;
+#else
+       ktime_get_real_ts(&ts);
+#endif
+
+       second = ts.tv_sec;
+       second -= sys_tz.tz_minuteswest * SECS_PER_MIN;
+
+       /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
+       if (second < UNIX_SECS_1980) {
+               tp->sec  = 0;
+               tp->min  = 0;
+               tp->hour = 0;
+               tp->day  = 1;
+               tp->mon  = 1;
+               tp->year = 0;
+               return tp;
+       }
+#if BITS_PER_LONG == 64
+       if (second >= UNIX_SECS_2108) {
+               tp->sec  = 59;
+               tp->min  = 59;
+               tp->hour = 23;
+               tp->day  = 31;
+               tp->mon  = 12;
+               tp->year = 127;
+               return tp;
+       }
+#endif
+
+       day = second / SECS_PER_DAY - DAYS_DELTA_DECADE;
+       year = day / 365;
+
+       MAKE_LEAP_YEAR(leap_day, year);
+       if (year * 365 + leap_day > day)
+               year--;
+
+       MAKE_LEAP_YEAR(leap_day, year);
+
+       day -= year * 365 + leap_day;
+
+       if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) {
+               month = 2;
+       } else {
+               if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3])
+                       day--;
+               for (month = 1; month < 12; month++) {
+                       if (accum_days_in_year[month + 1] > day)
+                               break;
+               }
+       }
+       day -= accum_days_in_year[month];
+
+       tp->sec  = second % SECS_PER_MIN;
+       tp->min  = (second / SECS_PER_MIN) % 60;
+       tp->hour = (second / SECS_PER_HOUR) % 24;
+       tp->day  = day + 1;
+       tp->mon  = month;
+       tp->year = year;
+
+       return tp;
+} /* end of tm_current */
diff --git a/fs/exfat/exfat_oal.h b/fs/exfat/exfat_oal.h
new file mode 100644 (file)
index 0000000..b6dd789
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_oal.h                                               */
+/*  PURPOSE : Header File for exFAT OS Adaptation Layer                 */
+/*            (Semaphore Functions & Real-Time Clock Functions)         */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#ifndef _EXFAT_OAL_H
+#define _EXFAT_OAL_H
+
+#include <linux/semaphore.h>
+#include "exfat_config.h"
+#include <linux/version.h>
+
+/*----------------------------------------------------------------------*/
+/*  Constant & Macro Definitions (Configurable)                         */
+/*----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+/*  Constant & Macro Definitions (Non-Configurable)                     */
+/*----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+/*  Type Definitions                                                    */
+/*----------------------------------------------------------------------*/
+
+typedef struct {
+       u16      sec;        /* 0 ~ 59               */
+       u16      min;        /* 0 ~ 59               */
+       u16      hour;       /* 0 ~ 23               */
+       u16      day;        /* 1 ~ 31               */
+       u16      mon;        /* 1 ~ 12               */
+       u16      year;       /* 0 ~ 127 (since 1980) */
+} TIMESTAMP_T;
+
+/*----------------------------------------------------------------------*/
+/*  External Function Declarations                                      */
+/*----------------------------------------------------------------------*/
+
+s32 sm_init(struct semaphore *sm);
+s32 sm_P(struct semaphore *sm);
+void  sm_V(struct semaphore *sm);
+
+TIMESTAMP_T *tm_current(TIMESTAMP_T *tm);
+
+#endif /* _EXFAT_OAL_H */
diff --git a/fs/exfat/exfat_super.c b/fs/exfat/exfat_super.c
new file mode 100644 (file)
index 0000000..ab828c3
--- /dev/null
@@ -0,0 +1,2697 @@
+/* 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>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
+#include <linux/aio.h>
+#endif
+#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_data.h"
+#include "exfat_oal.h"
+
+#include "exfat_blkdev.h"
+#include "exfat_cache.h"
+#include "exfat_nls.h"
+#include "exfat_api.h"
+#include "exfat_core.h"
+
+#include "exfat_super.h"
+
+static struct kmem_cache *exfat_inode_cachep;
+
+static int exfat_default_codepage = CONFIG_EXFAT_DEFAULT_CODEPAGE;
+static char exfat_default_iocharset[] = CONFIG_EXFAT_DEFAULT_IOCHARSET;
+
+extern struct timezone sys_tz;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0)
+#define current_time(x)        (CURRENT_TIME_SEC)
+#endif
+
+#define CHECK_ERR(x)   BUG_ON(x)
+
+#define UNIX_SECS_1980    315532800L
+
+#if BITS_PER_LONG == 64
+#define UNIX_SECS_2108    4354819200L
+#endif
+/* days between 1.1.70 and 1.1.80 (2 leap days) */
+#define DAYS_DELTA_DECADE    (365 * 10 + 2)
+/* 120 (2100 - 1980) isn't leap year */
+#define NO_LEAP_YEAR_2100    (120)
+#define IS_LEAP_YEAR(y)    (!((y) & 0x3) && (y) != NO_LEAP_YEAR_2100)
+
+#define SECS_PER_MIN    (60)
+#define SECS_PER_HOUR   (60 * SECS_PER_MIN)
+#define SECS_PER_DAY    (24 * SECS_PER_HOUR)
+
+#define MAKE_LEAP_YEAR(leap_year, year)                         \
+       do {                                                    \
+               if (unlikely(year > NO_LEAP_YEAR_2100))         \
+                       leap_year = ((year + 3) / 4) - 1;       \
+               else                                            \
+                       leap_year = ((year + 3) / 4);           \
+       } while (0)
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+static time_t accum_days_in_year[] = {
+       /* Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec */
+       0,   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0,
+};
+
+static void _exfat_truncate(struct inode *inode, loff_t old_size);
+
+/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
+void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec *ts,
+                                                DATE_TIME_T *tp)
+{
+       time_t year = tp->Year;
+       time_t ld;
+
+       MAKE_LEAP_YEAR(ld, year);
+
+       if (IS_LEAP_YEAR(year) && (tp->Month) > 2)
+               ld++;
+
+       ts->tv_sec =  tp->Second  + tp->Minute * SECS_PER_MIN
+                                 + tp->Hour * SECS_PER_HOUR
+                                 + (year * 365 + ld + accum_days_in_year[(tp->Month)] + (tp->Day - 1) + DAYS_DELTA_DECADE) * SECS_PER_DAY
+                                 + sys_tz.tz_minuteswest * SECS_PER_MIN;
+       ts->tv_nsec = 0;
+}
+
+/* Convert linear UNIX date to a FAT time/date pair. */
+void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec *ts,
+                                                DATE_TIME_T *tp)
+{
+       time_t second = ts->tv_sec;
+       time_t day, month, year;
+       time_t ld;
+
+       second -= sys_tz.tz_minuteswest * SECS_PER_MIN;
+
+       /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
+       if (second < UNIX_SECS_1980) {
+               tp->Second  = 0;
+               tp->Minute  = 0;
+               tp->Hour = 0;
+               tp->Day  = 1;
+               tp->Month  = 1;
+               tp->Year = 0;
+               return;
+       }
+#if (BITS_PER_LONG == 64)
+       if (second >= UNIX_SECS_2108) {
+               tp->Second  = 59;
+               tp->Minute  = 59;
+               tp->Hour = 23;
+               tp->Day  = 31;
+               tp->Month  = 12;
+               tp->Year = 127;
+               return;
+       }
+#endif
+       day = second / SECS_PER_DAY - DAYS_DELTA_DECADE;
+       year = day / 365;
+       MAKE_LEAP_YEAR(ld, year);
+       if (year * 365 + ld > day)
+               year--;
+
+       MAKE_LEAP_YEAR(ld, year);
+       day -= year * 365 + ld;
+
+       if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) {
+               month = 2;
+       } else {
+               if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3])
+                       day--;
+               for (month = 1; month < 12; month++) {
+                       if (accum_days_in_year[month + 1] > day)
+                               break;
+               }
+       }
+       day -= accum_days_in_year[month];
+
+       tp->Second  = second % SECS_PER_MIN;
+       tp->Minute  = (second / SECS_PER_MIN) % 60;
+       tp->Hour = (second / SECS_PER_HOUR) % 24;
+       tp->Day  = day + 1;
+       tp->Month  = month;
+       tp->Year = year;
+}
+
+static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos);
+#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);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)
+static int exfat_write_inode(struct inode *inode, int wait);
+#else
+static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc);
+#endif
+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_revalidate(struct dentry *dentry)
+{
+       return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00)
+static int exfat_revalidate(struct dentry *dentry, unsigned int flags)
+#else
+static int exfat_revalidate(struct dentry *dentry, struct nameidata *nd)
+#endif
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00)
+       if (flags & LOOKUP_RCU)
+               return -ECHILD;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,00)
+       if (nd && nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+#endif
+
+       if (dentry->d_inode)
+               return 1;
+       return __exfat_revalidate(dentry);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00)
+static int exfat_revalidate_ci(struct dentry *dentry, unsigned int flags)
+#else
+static int exfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd)
+#endif
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00)
+       if (flags & LOOKUP_RCU)
+               return -ECHILD;
+#else
+       unsigned int flags;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,00)
+       if (nd && nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+#endif
+
+       flags = nd ? nd->flags : 0;
+#endif
+
+       if (dentry->d_inode)
+               return 1;
+
+       if (!flags)
+               return 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,00)
+       if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
+               return 0;
+#else
+       if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) {
+               if (nd->flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
+                       return 0;
+       }
+#endif
+
+       return __exfat_revalidate(dentry);
+}
+
+static unsigned int __exfat_striptail_len(unsigned int len, const char *name)
+{
+       while (len && name[len - 1] == '.')
+               len--;
+       return len;
+}
+
+static unsigned int exfat_striptail_len(const struct qstr *qstr)
+{
+       return __exfat_striptail_len(qstr->len, qstr->name);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+static int exfat_d_hash(const struct dentry *dentry, struct qstr *qstr)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
+static int exfat_d_hash(struct dentry *dentry, struct qstr *qstr)
+#else
+static int exfat_d_hash(const struct dentry *dentry, const struct inode *inode,
+               struct qstr *qstr)
+#endif
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)
+       qstr->hash = full_name_hash(dentry, qstr->name, exfat_striptail_len(qstr));
+#else
+       qstr->hash = full_name_hash(qstr->name, exfat_striptail_len(qstr));
+#endif
+       return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+static int exfat_d_hashi(const struct dentry *dentry, struct qstr *qstr)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
+static int exfat_d_hashi(struct dentry *dentry, struct qstr *qstr)
+#else
+static int exfat_d_hashi(const struct dentry *dentry, const struct inode *inode,
+               struct qstr *qstr)
+#endif
+{
+       struct super_block *sb = dentry->d_sb;
+       const unsigned char *name;
+       unsigned int len;
+       unsigned long hash;
+
+       name = qstr->name;
+       len = exfat_striptail_len(qstr);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)
+       hash = init_name_hash(dentry);
+#else
+       hash = init_name_hash();
+#endif
+       while (len--)
+               hash = partial_name_hash(nls_upper(sb, *name++), hash);
+       qstr->hash = end_name_hash(hash);
+
+       return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)
+static int exfat_cmpi(const struct dentry *dentry,
+               unsigned int len, const char *str, const struct qstr *name)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+static int exfat_cmpi(const struct dentry *parent, const struct dentry *dentry,
+               unsigned int len, const char *str, const struct qstr *name)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
+static int exfat_cmpi(struct dentry *parent, struct qstr *a, struct qstr *b)
+#else
+static int exfat_cmpi(const struct dentry *parent, const struct inode *pinode,
+               const struct dentry *dentry, const struct inode *inode,
+               unsigned int len, const char *str, const struct qstr *name)
+#endif
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)
+       struct nls_table *t = EXFAT_SB(dentry->d_sb)->nls_io;
+#else
+       struct nls_table *t = EXFAT_SB(parent->d_sb)->nls_io;
+#endif
+       unsigned int alen, blen;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
+       alen = exfat_striptail_len(a);
+       blen = exfat_striptail_len(b);
+#else
+       alen = exfat_striptail_len(name);
+       blen = __exfat_striptail_len(len, str);
+#endif
+       if (alen == blen) {
+               if (t == NULL) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
+                       if (strncasecmp(a->name, b->name, alen) == 0)
+#else
+                       if (strncasecmp(name->name, str, alen) == 0)
+#endif
+                               return 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
+               } else if (nls_strnicmp(t, a->name, b->name, alen) == 0)
+#else
+               } else if (nls_strnicmp(t, name->name, str, alen) == 0)
+#endif
+                       return 0;
+       }
+       return 1;
+}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)
+static int exfat_cmp(const struct dentry *dentry,
+               unsigned int len, const char *str, const struct qstr *name)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+static int exfat_cmp(const struct dentry *parent, const struct dentry *dentry,
+               unsigned int len, const char *str, const struct qstr *name)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
+static int exfat_cmp(struct dentry *parent, struct qstr *a,
+                       struct qstr *b)
+#else
+static int exfat_cmp(const struct dentry *parent, const struct inode *pinode,
+               const struct dentry *dentry, const struct inode *inode,
+               unsigned int len, const char *str, const struct qstr *name)
+#endif
+{
+       unsigned int alen, blen;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
+       alen = exfat_striptail_len(a);
+       blen = exfat_striptail_len(b);
+#else
+       alen = exfat_striptail_len(name);
+       blen = __exfat_striptail_len(len, str);
+#endif
+       if (alen == blen) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
+               if (strncmp(a->name, b->name, alen) == 0)
+#else
+               if (strncmp(name->name, str, alen) == 0)
+#endif
+                       return 0;
+       }
+       return 1;
+}
+
+static const struct dentry_operations exfat_ci_dentry_ops = {
+       .d_revalidate   = exfat_revalidate_ci,
+       .d_hash         = exfat_d_hashi,
+       .d_compare      = exfat_cmpi,
+};
+
+static const struct dentry_operations exfat_dentry_ops = {
+       .d_revalidate   = exfat_revalidate,
+       .d_hash         = exfat_d_hash,
+       .d_compare      = exfat_cmp,
+};
+
+/*======================================================================*/
+/*  Directory Entry Operations                                          */
+/*======================================================================*/
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+static int exfat_readdir(struct file *filp, struct dir_context *ctx)
+#else
+static int exfat_readdir(struct file *filp, void *dirent, filldir_t filldir)
+#endif
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)
+       struct inode *inode = file_inode(filp);
+#else
+       struct inode *inode = filp->f_path.dentry->d_inode;
+#endif
+       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);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+       cpos = ctx->pos;
+#else
+       cpos = filp->f_pos;
+#endif
+       /* Fake . and .. for the root directory. */
+       if ((p_fs->vol_type == EXFAT) || (inode->i_ino == EXFAT_ROOT_INO)) {
+               while (cpos < 2) {
+                       if (inode->i_ino == EXFAT_ROOT_INO)
+                               inum = EXFAT_ROOT_INO;
+                       else if (cpos == 0)
+                               inum = inode->i_ino;
+                       else /* (cpos == 1) */
+                               inum = parent_ino(filp->f_path.dentry);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+                       if (!dir_emit_dots(filp, ctx))
+#else
+                       if (filldir(dirent, "..", cpos+1, cpos, inum, DT_DIR) < 0)
+#endif
+                               goto out;
+                       cpos++;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+                       ctx->pos++;
+#else
+                       filp->f_pos++;
+#endif
+               }
+               if (cpos == 2)
+                       cpos = 0;
+       }
+       if (cpos & (DENTRY_SIZE - 1)) {
+               err = -ENOENT;
+               goto out;
+       }
+
+get_new:
+       EXFAT_I(inode)->fid.size = i_size_read(inode);
+       EXFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS;
+
+       err = FsReadDir(inode, &de);
+       if (err) {
+               /* at least we tried to read a sector
+                * move cpos to next sector position (should be aligned)
+                */
+               if (err == FFS_MEDIAERR) {
+                       cpos += 1 << p_bd->sector_size_bits;
+                       cpos &= ~((1 << p_bd->sector_size_bits)-1);
+               }
+
+               err = -EIO;
+               goto end_of_dir;
+       }
+
+       cpos = EXFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS;
+
+       if (!de.Name[0])
+               goto end_of_dir;
+
+       if (!memcmp(de.ShortName, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) {
+               inum = inode->i_ino;
+       } else if (!memcmp(de.ShortName, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) {
+               inum = parent_ino(filp->f_path.dentry);
+       } else {
+               loff_t i_pos = ((loff_t) EXFAT_I(inode)->fid.start_clu << 32) |
+                                          ((EXFAT_I(inode)->fid.rwoffset-1) & 0xffffffff);
+
+               struct inode *tmp = exfat_iget(sb, i_pos);
+               if (tmp) {
+                       inum = tmp->i_ino;
+                       iput(tmp);
+               } else {
+                       inum = iunique(sb, EXFAT_ROOT_INO);
+               }
+       }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+       if (!dir_emit(ctx, de.Name, strlen(de.Name), inum,
+               (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG))
+#else
+       if (filldir(dirent, de.Name, strlen(de.Name), cpos-1, inum,
+                               (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG) < 0)
+#endif
+               goto out;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+       ctx->pos = cpos;
+#else
+       filp->f_pos = cpos;
+#endif
+       goto get_new;
+
+end_of_dir:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+       ctx->pos = cpos;
+#else
+       filp->f_pos = cpos;
+#endif
+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 !(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36))
+    #if !(LINUX_VERSION_CODE < KERNEL_VERSION(3,18,3))
+                 struct inode *inode = filp->f_path.dentry->d_inode;
+    #else
+                 struct inode *inode = filp->f_dentry->d_inode;
+       #endif
+#endif
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+       unsigned int flags;
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+
+       switch (cmd) {
+       case EXFAT_IOCTL_GET_VOLUME_ID:
+               return exfat_ioctl_volume_id(inode);
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+       case EXFAT_IOC_GET_DEBUGFLAGS: {
+               struct super_block *sb = inode->i_sb;
+               struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+               flags = sbi->debug_flags;
+               return put_user(flags, (int __user *)arg);
+       }
+       case EXFAT_IOC_SET_DEBUGFLAGS: {
+               struct super_block *sb = inode->i_sb;
+               struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+
+               if (get_user(flags, (int __user *) arg))
+                       return -EFAULT;
+
+               __lock_super(sb);
+               sbi->debug_flags = flags;
+               __unlock_super(sb);
+
+               return 0;
+       }
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+       default:
+               return -ENOTTY; /* Inappropriate ioctl for device */
+       }
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+static int exfat_file_fsync(struct file *filp, struct dentry *dentry,
+                               int datasync)
+#else
+static int exfat_file_fsync(struct file *filp, int datasync)
+#endif
+{
+       struct inode *inode = filp->f_mapping->host;
+       struct super_block *sb = inode->i_sb;
+       int res, err;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+       res = simple_fsync(filp, dentry, datasync);
+#else
+       res = generic_file_fsync(filp, datasync);
+#endif
+       err = FsSyncVol(sb, 1);
+
+       return res ? res : err;
+}
+#endif
+
+const struct file_operations exfat_dir_operations = {
+       .llseek     = generic_file_llseek,
+       .read       = generic_read_dir,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+       .iterate    = exfat_readdir,
+#else
+       .readdir    = exfat_readdir,
+#endif
+#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,3,0)
+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;
+       FILE_ID_T fid;
+       loff_t i_pos;
+       int err;
+
+       __lock_super(sb);
+
+       DPRINTK("exfat_create entered\n");
+
+       err = FsCreateFile(dir, (u8 *) 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 = current_time(dir);
+       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 = current_time(inode);
+       /* timestamp is already written, so mark_inode_dirty() is unnecessary. */
+
+       dentry->d_time = dentry->d_parent->d_inode->i_version;
+       d_instantiate(dentry, inode);
+
+out:
+       __unlock_super(sb);
+       DPRINTK("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, (u8 *) 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;
+       u64 ret;
+       mode_t i_mode;
+
+       __lock_super(sb);
+       DPRINTK("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) {
+               EXFAT_I(inode)->target = kmalloc(i_size_read(inode)+1, GFP_KERNEL);
+               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);
+               DPRINTK("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
+       DPRINTK("exfat_lookup exited 2\n");
+       return dentry;
+
+error:
+       __unlock_super(sb);
+       DPRINTK("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;
+       int err;
+
+       __lock_super(sb);
+
+       DPRINTK("exfat_unlink entered\n");
+
+       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 = current_time(dir);
+       if (IS_DIRSYNC(dir))
+               (void) exfat_sync_inode(dir);
+       else
+               mark_inode_dirty(dir);
+
+       clear_nlink(inode);
+       inode->i_mtime = inode->i_atime = current_time(inode);
+       exfat_detach(inode);
+       remove_inode_hash(inode);
+
+out:
+       __unlock_super(sb);
+       DPRINTK("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;
+       FILE_ID_T fid;
+       loff_t i_pos;
+       int err;
+       u64 len = (u64) strlen(target);
+       u64 ret;
+
+       __lock_super(sb);
+
+       DPRINTK("exfat_symlink entered\n");
+
+       err = FsCreateFile(dir, (u8 *) 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 = current_time(dir);
+       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 = current_time(inode);
+       /* timestamp is already written, so mark_inode_dirty() is unneeded. */
+
+       EXFAT_I(inode)->target = kmalloc(len+1, GFP_KERNEL);
+       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);
+       DPRINTK("exfat_symlink exited\n");
+       return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)
+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;
+       FILE_ID_T fid;
+       loff_t i_pos;
+       int err;
+
+       __lock_super(sb);
+
+       DPRINTK("exfat_mkdir entered\n");
+
+       err = FsCreateDir(dir, (u8 *) 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 = current_time(dir);
+       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 = current_time(inode);
+       /* timestamp is already written, so mark_inode_dirty() is unneeded. */
+
+       dentry->d_time = dentry->d_parent->d_inode->i_version;
+       d_instantiate(dentry, inode);
+
+out:
+       __unlock_super(sb);
+       DPRINTK("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;
+       int err;
+
+       __lock_super(sb);
+
+       DPRINTK("exfat_rmdir entered\n");
+
+       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 = current_time(dir);
+       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 = current_time(inode);
+       exfat_detach(inode);
+       remove_inode_hash(inode);
+
+out:
+       __unlock_super(sb);
+       DPRINTK("exfat_rmdir exited\n");
+       return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)
+static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
+                                               struct inode *new_dir, struct dentry *new_dentry,
+                                               unsigned int flags)
+#else
+static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
+                                               struct inode *new_dir, struct dentry *new_dentry)
+#endif
+{
+       struct inode *old_inode, *new_inode;
+       struct super_block *sb = old_dir->i_sb;
+       loff_t i_pos;
+       int err;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)
+       if (flags)
+               return -EINVAL;
+#endif
+
+       __lock_super(sb);
+
+       DPRINTK("exfat_rename entered\n");
+
+       old_inode = old_dentry->d_inode;
+       new_inode = new_dentry->d_inode;
+
+       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 = current_time(new_dir);
+       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 = current_time(old_dir);
+       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 = current_time(new_inode);
+       }
+
+out:
+       __unlock_super(sb);
+       DPRINTK("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;
+
+       err = generic_cont_expand_simple(inode, size);
+       if (err != 0)
+               return err;
+
+       inode->i_ctime = inode->i_mtime = current_time(inode);
+       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)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
+                       err =  wait_on_page_writeback_range(mapping,
+                                       start >> PAGE_CACHE_SHIFT,
+                                       (start + count - 1) >> PAGE_CACHE_SHIFT);
+#else
+                       err =  filemap_fdatawait_range(mapping, start, start + count - 1);
+#endif
+       }
+       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 LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+       if (!uid_eq(current_fsuid(), inode->i_uid))
+#else
+       if (current_fsuid() != inode->i_uid)
+#endif
+       {
+               if (in_group_p(inode->i_gid))
+                       allow_utime >>= 3;
+               if (allow_utime & MAY_WRITE)
+                       return 1;
+       }
+
+       /* use a default check */
+       return 0;
+}
+
+static int exfat_sanitize_mode(const struct exfat_sb_info *sbi,
+                                                          struct inode *inode, umode_t *mode_ptr)
+{
+       mode_t i_mode, mask, perm;
+
+       i_mode = inode->i_mode;
+
+       if (S_ISREG(i_mode) || S_ISLNK(i_mode))
+               mask = sbi->options.fs_fmask;
+       else
+               mask = sbi->options.fs_dmask;
+
+       perm = *mode_ptr & ~(S_IFMT | mask);
+
+       /* Of the r and x bits, all (subject to umask) must be present.*/
+       if ((perm & (S_IRUGO | S_IXUGO)) != (i_mode & (S_IRUGO|S_IXUGO)))
+               return -EPERM;
+
+       if (exfat_mode_can_hold_ro(inode)) {
+               /* Of the w bits, either all (subject to umask) or none must be present. */
+               if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask)))
+                       return -EPERM;
+       } else {
+               /* If exfat_mode_can_hold_ro(inode) is false, can't change w bits. */
+               if ((perm & S_IWUGO) != (S_IWUGO & ~mask))
+                       return -EPERM;
+       }
+
+       *mode_ptr &= S_IFMT | perm;
+
+       return 0;
+}
+
+static int exfat_setattr(struct dentry *dentry, struct iattr *attr)
+{
+
+       struct exfat_sb_info *sbi = EXFAT_SB(dentry->d_sb);
+       struct inode *inode = dentry->d_inode;
+       unsigned int ia_valid;
+       int error;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)
+       loff_t old_size;
+#endif
+
+       DPRINTK("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);
+       }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)
+       error = setattr_prepare(dentry, attr);
+#else
+       error = inode_change_ok(inode, attr);
+#endif
+       attr->ia_valid = ia_valid;
+       if (error)
+               return error;
+
+       if (((attr->ia_valid & ATTR_UID) &&
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+                (!uid_eq(attr->ia_uid, sbi->options.fs_uid))) ||
+               ((attr->ia_valid & ATTR_GID) &&
+                (!gid_eq(attr->ia_gid, sbi->options.fs_gid))) ||
+#else
+                (attr->ia_uid != sbi->options.fs_uid)) ||
+               ((attr->ia_valid & ATTR_GID) &&
+                (attr->ia_gid != sbi->options.fs_gid)) ||
+#endif
+               ((attr->ia_valid & ATTR_MODE) &&
+                (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | S_IRWXUGO)))) {
+               return -EPERM;
+       }
+
+       /*
+        * We don't return -EPERM here. Yes, strange, but this is too
+        * old behavior.
+        */
+       if (attr->ia_valid & ATTR_MODE) {
+               if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0)
+                       attr->ia_valid &= ~ATTR_MODE;
+       }
+
+       EXFAT_I(inode)->fid.size = i_size_read(inode);
+
+#if 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
+
+       DPRINTK("exfat_setattr exited\n");
+       return error;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+static int exfat_getattr(const struct path *path, struct kstat *stat,
+                        u32 request_mask, unsigned int flags)
+{
+       struct inode *inode = path->dentry->d_inode;
+#else
+static int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+{
+       struct inode *inode = dentry->d_inode;
+#endif
+
+       DPRINTK("exfat_getattr entered\n");
+
+       generic_fillattr(inode, stat);
+       stat->blksize = EXFAT_SB(inode->i_sb)->fs_info.cluster_size;
+
+       DPRINTK("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,
+};
+
+/*======================================================================*/
+/*  File Operations                                                     */
+/*======================================================================*/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0)
+static const char *exfat_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done)
+{
+       struct exfat_inode_info *ei = EXFAT_I(inode);
+       if (ei->target != NULL) {
+               char *cookie = ei->target;
+               if (cookie != NULL) {
+                       return (char *)(ei->target);
+               }
+       }
+       return NULL;
+}
+#elif LINUX_VERSION_CODE > KERNEL_VERSION(4,1,0)
+static const char *exfat_follow_link(struct dentry *dentry, void **cookie)
+{
+       struct exfat_inode_info *ei = EXFAT_I(dentry->d_inode);
+       return *cookie = (char *)(ei->target);
+}
+#else
+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;
+}
+#endif
+
+const struct inode_operations exfat_symlink_inode_operations = {
+       #if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
+               .readlink    = generic_readlink,
+       #endif
+       #if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
+               .follow_link = exfat_follow_link,
+       #endif
+       #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0)
+               .get_link = exfat_get_link,
+       #endif
+};
+
+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,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)
+       .read        = do_sync_read,
+       .write       = do_sync_write,
+       .aio_read    = generic_file_aio_read,
+       .aio_write   = generic_file_aio_write,
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0)
+       .read        = new_sync_read,
+       .write       = new_sync_write,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
+       .read_iter   = generic_file_read_iter,
+       .write_iter  = generic_file_write_iter,
+#endif
+       .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);
+
+       /*
+        * This protects against truncating a file bigger than it was then
+        * trying to write into the hole.
+        */
+       if (EXFAT_I(inode)->mmu_private > i_size_read(inode))
+               EXFAT_I(inode)->mmu_private = i_size_read(inode);
+
+       if (EXFAT_I(inode)->fid.start_clu == 0)
+               goto out;
+
+       err = FsTruncateFile(inode, old_size, i_size_read(inode));
+       if (err)
+               goto out;
+
+       inode->i_ctime = inode->i_mtime = current_time(inode);
+       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,
+};
+
+/*======================================================================*/
+/*  Address Space Operations                                            */
+/*======================================================================*/
+
+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;  /* cluster offset */
+       sec_offset = sector & (p_fs->sectors_per_clu - 1);  /* sector offset in cluster */
+
+       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)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0)
+               truncate_pagecache(inode, i_size_read(inode));
+#else
+               truncate_pagecache(inode, to, i_size_read(inode));
+#endif
+               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(inode);
+               fid->attr |= ATTR_ARCHIVE;
+               mark_inode_dirty(inode);
+       }
+       return err;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)
+#ifdef CONFIG_AIO_OPTIMIZATION
+static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb,
+                                               struct iov_iter *iter, loff_t offset)
+#else
+static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb,
+                                          const struct iovec *iov,
+                                          loff_t offset, unsigned long nr_segs)
+#endif
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0)
+static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb,
+                                          struct iov_iter *iter, loff_t offset)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+static ssize_t exfat_direct_IO(struct kiocb *iocb,
+                                          struct iov_iter *iter, loff_t offset)
+#else /* >= 4.7.x */
+static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
+#endif
+{
+       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 LINUX_VERSION_CODE >= KERNEL_VERSION(4,2,0)
+       int rw;
+
+       rw = iov_iter_rw(iter);
+#endif
+
+       if (rw == WRITE) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)
+#ifdef CONFIG_AIO_OPTIMIZATION
+               if (EXFAT_I(inode)->mmu_private <
+                                       (offset + iov_iter_count(iter)))
+#else
+               if (EXFAT_I(inode)->mmu_private < (offset + iov_length(iov, nr_segs)))
+#endif
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+               if (EXFAT_I(inode)->mmu_private < (offset + iov_iter_count(iter)))
+#else
+               if (EXFAT_I(inode)->mmu_private < iov_iter_count(iter))
+#endif
+                       return 0;
+       }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
+       ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
+       ret = blockdev_direct_IO(iocb, inode, iter,
+                                       offset, exfat_get_block);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
+       ret = blockdev_direct_IO(rw, iocb, inode, iter,
+                                       offset, exfat_get_block);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)
+#ifdef CONFIG_AIO_OPTIMIZATION
+       ret = blockdev_direct_IO(rw, iocb, inode, iter,
+                                       offset, exfat_get_block);
+#else
+       ret = blockdev_direct_IO(rw, iocb, inode, iov,
+                                       offset, nr_segs, exfat_get_block);
+#endif
+#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(4,7,0)
+       if ((ret < 0) && (rw & WRITE))
+               exfat_write_failed(mapping, iov_iter_count(iter));
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
+       if ((ret < 0) && (rw & WRITE))
+               exfat_write_failed(mapping, offset+iov_iter_count(iter));
+#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34)
+       if ((ret < 0) && (rw & WRITE))
+#ifdef CONFIG_AIO_OPTIMIZATION
+               exfat_write_failed(mapping, offset+iov_iter_count(iter));
+#else
+               exfat_write_failed(mapping, offset+iov_length(iov, nr_segs));
+#endif
+#endif
+       return ret;
+}
+
+static sector_t _exfat_bmap(struct address_space *mapping, sector_t block)
+{
+       sector_t blocknr;
+
+       /* exfat_get_cluster() assumes the requested blocknr isn't truncated. */
+#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(&EXFAT_I(mapping->host)->i_alloc_sem);
+       blocknr = generic_block_bmap(mapping, block, exfat_get_block);
+       up_read(&EXFAT_I(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
+};
+
+/*======================================================================*/
+/*  Super Operations                                                    */
+/*======================================================================*/
+
+static inline unsigned long exfat_hash(loff_t i_pos)
+{
+       return hash_32(i_pos, EXFAT_HASH_BITS);
+}
+
+static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos)
+{
+       struct exfat_sb_info *sbi = EXFAT_SB(sb);
+       struct exfat_inode_info *info;
+       struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos);
+       struct inode *inode = NULL;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
+       struct hlist_node *node;
+
+       spin_lock(&sbi->inode_hash_lock);
+       hlist_for_each_entry(info, node, head, i_hash_fat) {
+#else
+       spin_lock(&sbi->inode_hash_lock);
+       hlist_for_each_entry(info, head, i_hash_fat) {
+#endif
+               CHECK_ERR(info->vfs_inode.i_sb != sb);
+
+               if (i_pos != info->i_pos)
+                       continue;
+               inode = igrab(&info->vfs_inode);
+               if (inode)
+                       break;
+       }
+       spin_unlock(&sbi->inode_hash_lock);
+       return inode;
+}
+
+static void exfat_attach(struct inode *inode, loff_t i_pos)
+{
+       struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+       struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos);
+
+       spin_lock(&sbi->inode_hash_lock);
+       EXFAT_I(inode)->i_pos = i_pos;
+       hlist_add_head(&EXFAT_I(inode)->i_hash_fat, head);
+       spin_unlock(&sbi->inode_hash_lock);
+}
+
+static void exfat_detach(struct inode *inode)
+{
+       struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+
+       spin_lock(&sbi->inode_hash_lock);
+       hlist_del_init(&EXFAT_I(inode)->i_hash_fat);
+       EXFAT_I(inode)->i_pos = 0;
+       spin_unlock(&sbi->inode_hash_lock);
+}
+
+/* doesn't deal with root inode */
+static int exfat_fill_inode(struct inode *inode, FILE_ID_T *fid)
+{
+       struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+       FS_INFO_T *p_fs = &(sbi->fs_info);
+       DIR_ENTRY_T info;
+
+       memcpy(&(EXFAT_I(inode)->fid), fid, sizeof(FILE_ID_T));
+
+       FsReadStat(inode, &info);
+
+       EXFAT_I(inode)->i_pos = 0;
+       EXFAT_I(inode)->target = NULL;
+       inode->i_uid = sbi->options.fs_uid;
+       inode->i_gid = sbi->options.fs_gid;
+       inode->i_version++;
+       inode->i_generation = get_seconds();
+
+       if (info.Attr & ATTR_SUBDIR) { /* directory */
+               inode->i_generation &= ~1;
+               inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO);
+               inode->i_op = &exfat_dir_inode_operations;
+               inode->i_fop = &exfat_dir_operations;
+
+               i_size_write(inode, info.Size);
+               EXFAT_I(inode)->mmu_private = i_size_read(inode);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,00)
+               set_nlink(inode, info.NumSubdirs);
+#else
+               inode->i_nlink = info.NumSubdirs;
+#endif
+       } else if (info.Attr & ATTR_SYMLINK) { /* symbolic link */
+               inode->i_generation |= 1;
+               inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO);
+               inode->i_op = &exfat_symlink_inode_operations;
+
+               i_size_write(inode, info.Size);
+               EXFAT_I(inode)->mmu_private = i_size_read(inode);
+       } else { /* regular file */
+               inode->i_generation |= 1;
+               inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO);
+               inode->i_op = &exfat_file_inode_operations;
+               inode->i_fop = &exfat_file_operations;
+               inode->i_mapping->a_ops = &exfat_aops;
+               inode->i_mapping->nrpages = 0;
+
+               i_size_write(inode, info.Size);
+               EXFAT_I(inode)->mmu_private = i_size_read(inode);
+       }
+       exfat_save_attr(inode, info.Attr);
+
+       inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1))
+                                          & ~((loff_t)p_fs->cluster_size - 1)) >> 9;
+
+       exfat_time_fat2unix(sbi, &inode->i_mtime, &info.ModifyTimestamp);
+       exfat_time_fat2unix(sbi, &inode->i_ctime, &info.CreateTimestamp);
+       exfat_time_fat2unix(sbi, &inode->i_atime, &info.AccessTimestamp);
+
+       return 0;
+}
+
+static struct inode *exfat_build_inode(struct super_block *sb,
+                                                                          FILE_ID_T *fid, loff_t i_pos) {
+       struct inode *inode;
+       int err;
+
+       inode = exfat_iget(sb, i_pos);
+       if (inode)
+               goto out;
+       inode = new_inode(sb);
+       if (!inode) {
+               inode = ERR_PTR(-ENOMEM);
+               goto out;
+       }
+       inode->i_ino = iunique(sb, EXFAT_ROOT_INO);
+       inode->i_version = 1;
+       err = exfat_fill_inode(inode, fid);
+       if (err) {
+               iput(inode);
+               inode = ERR_PTR(err);
+               goto out;
+       }
+       exfat_attach(inode, i_pos);
+       insert_inode_hash(inode);
+out:
+       return inode;
+}
+
+static int exfat_sync_inode(struct inode *inode)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)
+       return exfat_write_inode(inode, 0);
+#else
+       return exfat_write_inode(inode, NULL);
+#endif
+}
+
+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)
+{
+       if (EXFAT_I(inode)->target)
+               kfree(EXFAT_I(inode)->target);
+       EXFAT_I(inode)->target = NULL;
+
+       kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode));
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)
+static int exfat_write_inode(struct inode *inode, int wait)
+#else
+static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
+#endif
+{
+       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,5,0)
+       end_writeback(inode);
+#else
+       clear_inode(inode);
+#endif
+       exfat_detach(inode);
+
+       remove_inode_hash(inode);
+}
+#endif
+
+static void exfat_free_super(struct exfat_sb_info *sbi)
+{
+       if (sbi->nls_disk)
+               unload_nls(sbi->nls_disk);
+       if (sbi->nls_io)
+               unload_nls(sbi->nls_io);
+       if (sbi->options.iocharset != exfat_default_iocharset)
+               kfree(sbi->options.iocharset);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+       /* mutex_init is in exfat_fill_super function. only for 3.7+ */
+       mutex_destroy(&sbi->s_lock);
+#endif
+       kfree(sbi);
+}
+
+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);
+
+       sb->s_fs_info = NULL;
+       exfat_free_super(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 == (u32) ~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)
+                       printk("[EXFAT] statfs on device is ejected\n");
+       }
+
+       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,3,0)
+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 LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+       if (__kuid_val(opts->fs_uid))
+               seq_printf(m, ",uid=%u", __kuid_val(opts->fs_uid));
+       if (__kgid_val(opts->fs_gid))
+               seq_printf(m, ",gid=%u", __kgid_val(opts->fs_gid));
+#else
+       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);
+#endif
+       seq_printf(m, ",fmask=%04o", opts->fs_fmask);
+       seq_printf(m, ",dmask=%04o", opts->fs_dmask);
+       if (opts->allow_utime)
+               seq_printf(m, ",allow_utime=%04o", opts->allow_utime);
+       if (sbi->nls_disk)
+               seq_printf(m, ",codepage=%s", sbi->nls_disk->charset);
+       if (sbi->nls_io)
+               seq_printf(m, ",iocharset=%s", sbi->nls_io->charset);
+       seq_printf(m, ",namecase=%u", opts->casesensitive);
+       if (opts->errors == EXFAT_ERRORS_CONT)
+               seq_puts(m, ",errors=continue");
+       else if (opts->errors == EXFAT_ERRORS_PANIC)
+               seq_puts(m, ",errors=panic");
+       else
+               seq_puts(m, ",errors=remount-ro");
+#ifdef CONFIG_EXFAT_DISCARD
+       if (opts->discard)
+               seq_printf(m, ",discard");
+#endif
+       return 0;
+}
+
+const struct super_operations exfat_sops = {
+       .alloc_inode   = exfat_alloc_inode,
+       .destroy_inode = exfat_destroy_inode,
+       .write_inode   = exfat_write_inode,
+#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,0)
+       .write_super   = exfat_write_super,
+#endif
+       .sync_fs       = exfat_sync_fs,
+       .statfs        = exfat_statfs,
+       .remount_fs    = exfat_remount,
+       .show_options  = exfat_show_options,
+};
+
+/*======================================================================*/
+/*  Export Operations                                                   */
+/*======================================================================*/
+
+static struct inode *exfat_nfs_get_inode(struct super_block *sb,
+                                      u64 ino, u32 generation)
+{
+       struct inode *inode = NULL;
+       if (ino < EXFAT_ROOT_INO)
+               return inode;
+       inode = ilookup(sb, ino);
+
+       if (inode && generation && (inode->i_generation != generation)) {
+               iput(inode);
+               inode = NULL;
+       }
+
+       return inode;
+}
+
+static struct dentry *exfat_fh_to_dentry(struct super_block *sb, struct fid *fid,
+                               int fh_len, int fh_type)
+{
+       return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
+                                   exfat_nfs_get_inode);
+}
+
+static struct dentry *exfat_fh_to_parent(struct super_block *sb, struct fid *fid,
+                               int fh_len, int fh_type)
+{
+       return generic_fh_to_parent(sb, fid, fh_len, fh_type,
+                                   exfat_nfs_get_inode);
+}
+
+const struct export_operations exfat_export_ops = {
+       .fh_to_dentry   = exfat_fh_to_dentry,
+       .fh_to_parent   = exfat_fh_to_parent,
+};
+
+/*======================================================================*/
+/*  Super Block Read Operations                                         */
+/*======================================================================*/
+
+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_utf8_hack,
+       Opt_err,
+#ifdef CONFIG_EXFAT_DISCARD
+       Opt_discard,
+#endif /* EXFAT_CONFIG_DISCARD */
+};
+
+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"},
+       {Opt_utf8_hack, "utf8"},
+#ifdef CONFIG_EXFAT_DISCARD
+       {Opt_discard, "discard"},
+#endif /* CONFIG_EXFAT_DISCARD */
+       {Opt_err, NULL}
+};
+
+static int parse_options(char *options, int silent, int *debug,
+                                                struct exfat_mount_options *opts)
+{
+       char *p;
+       substring_t args[MAX_OPT_ARGS];
+       int option;
+       char *iocharset;
+
+       opts->fs_uid = current_uid();
+       opts->fs_gid = current_gid();
+       opts->fs_fmask = opts->fs_dmask = current->fs->umask;
+       opts->allow_utime = (unsigned short) -1;
+       opts->codepage = exfat_default_codepage;
+       opts->iocharset = exfat_default_iocharset;
+       opts->casesensitive = 0;
+       opts->errors = EXFAT_ERRORS_RO;
+#ifdef CONFIG_EXFAT_DISCARD
+       opts->discard = 0;
+#endif
+       *debug = 0;
+
+       if (!options)
+               goto out;
+
+       while ((p = strsep(&options, ",")) != NULL) {
+               int token;
+               if (!*p)
+                       continue;
+
+               token = match_token(p, exfat_tokens, args);
+               switch (token) {
+               case Opt_uid:
+                       if (match_int(&args[0], &option))
+                               return 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+                       opts->fs_uid = KUIDT_INIT(option);
+#else
+                       opts->fs_uid = option;
+#endif
+                       break;
+               case Opt_gid:
+                       if (match_int(&args[0], &option))
+                               return 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+                       opts->fs_gid = KGIDT_INIT(option);
+#else
+                       opts->fs_gid = option;
+#endif
+                       break;
+               case Opt_umask:
+               case Opt_dmask:
+               case Opt_fmask:
+                       if (match_octal(&args[0], &option))
+                               return 0;
+                       if (token != Opt_dmask)
+                               opts->fs_fmask = option;
+                       if (token != Opt_fmask)
+                               opts->fs_dmask = option;
+                       break;
+               case Opt_allow_utime:
+                       if (match_octal(&args[0], &option))
+                               return 0;
+                       opts->allow_utime = option & (S_IWGRP | S_IWOTH);
+                       break;
+               case Opt_codepage:
+                       if (match_int(&args[0], &option))
+                               return 0;
+                       opts->codepage = option;
+                       break;
+               case Opt_charset:
+                       if (opts->iocharset != exfat_default_iocharset)
+                               kfree(opts->iocharset);
+                       iocharset = match_strdup(&args[0]);
+                       if (!iocharset)
+                               return -ENOMEM;
+                       opts->iocharset = iocharset;
+                       break;
+               case Opt_namecase:
+                       if (match_int(&args[0], &option))
+                               return 0;
+                       opts->casesensitive = option;
+                       break;
+               case Opt_err_cont:
+                       opts->errors = EXFAT_ERRORS_CONT;
+                       break;
+               case Opt_err_panic:
+                       opts->errors = EXFAT_ERRORS_PANIC;
+                       break;
+               case Opt_err_ro:
+                       opts->errors = EXFAT_ERRORS_RO;
+                       break;
+               case Opt_debug:
+                       *debug = 1;
+                       break;
+#ifdef CONFIG_EXFAT_DISCARD
+               case Opt_discard:
+                       opts->discard = 1;
+                       break;
+#endif /* CONFIG_EXFAT_DISCARD */
+               case Opt_utf8_hack:
+                       break;
+               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);
+       FS_INFO_T *p_fs = &(sbi->fs_info);
+       DIR_ENTRY_T info;
+
+       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 = current_time(inode);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,00)
+       set_nlink(inode, info.NumSubdirs + 2);
+#else
+       inode->i_nlink = info.NumSubdirs + 2;
+#endif
+
+       return 0;
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37)
+static void setup_dops(struct super_block *sb)
+{
+       if (EXFAT_SB(sb)->options.casesensitive == 0)
+               sb->s_d_op = &exfat_ci_dentry_ops;
+       else
+               sb->s_d_op = &exfat_dentry_ops;
+}
+#endif
+
+static int exfat_fill_super(struct super_block *sb, void *data, int silent)
+{
+       struct inode *root_inode = NULL;
+       struct exfat_sb_info *sbi;
+       int debug, ret;
+       long error;
+       char buf[50];
+
+       /*
+        * GFP_KERNEL is ok here, because while we do hold the
+        * supeblock lock, memory pressure can't call back into
+        * the filesystem, since we're only just about to mount
+        * it and have no inodes etc active!
+        */
+       sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL);
+       if (!sbi)
+               return -ENOMEM;
+#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;
+       sb->s_export_op = &exfat_export_ops;
+
+       error = parse_options(data, silent, &debug, &sbi->options);
+       if (error)
+               goto out_fail;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37)
+       setup_dops(sb);
+#endif
+
+       error = -EIO;
+       sb_min_blocksize(sb, 512);
+       sb->s_maxbytes = 0x7fffffffffffffffLL;    /* maximum file size */
+
+       ret = FsMountVol(sb);
+       if (ret) {
+               if (!silent)
+                       printk(KERN_ERR "[EXFAT] FsMountVol failed\n");
+
+               goto out_fail;
+       }
+
+       /* set up enough so that it can read an inode */
+       exfat_hash_init(sb);
+
+       /*
+        * The low byte of FAT's first entry must have same value with
+        * media-field.  But in real world, too many devices is
+        * writing wrong value.  So, removed that validity check.
+        *
+        * if (FAT_FIRST_ENT(sb, media) != first)
+        */
+
+       /* codepage is not meaningful in exfat */
+       if (sbi->fs_info.vol_type != EXFAT) {
+               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);
+
+       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);
+       sb->s_fs_info = NULL;
+       exfat_free_super(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)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+       /*
+        * Make sure all delayed rcu free inodes are flushed before we
+        * destroy cache.
+        */
+       rcu_barrier();
+#endif
+       kmem_cache_destroy(exfat_inode_cachep);
+}
+
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+static void exfat_debug_kill_sb(struct super_block *sb)
+{
+       struct exfat_sb_info *sbi = EXFAT_SB(sb);
+       struct block_device *bdev = sb->s_bdev;
+
+       long flags;
+
+       if (sbi) {
+               flags = sbi->debug_flags;
+
+               if (flags & EXFAT_DEBUGFLAGS_INVALID_UMOUNT) {
+                       /* invalidate_bdev drops all device cache include dirty.
+                          we use this to simulate device removal */
+                       FsReleaseCache(sb);
+                       invalidate_bdev(bdev);
+               }
+       }
+
+       kill_block_super(sb);
+}
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+
+static struct file_system_type exfat_fs_type = {
+       .owner       = THIS_MODULE,
+#if defined(CONFIG_MACH_LGE) || defined(CONFIG_HTC_BATT_CORE)
+       .name        = "texfat",
+#else
+       .name        = "exfat",
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+       .get_sb      = exfat_get_sb,
+#else
+       .mount       = exfat_fs_mount,
+#endif
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+       .kill_sb    = exfat_debug_kill_sb,
+#else
+       .kill_sb    = kill_block_super,
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+       .fs_flags    = FS_REQUIRES_DEV,
+};
+
+static int __init init_exfat(void)
+{
+       int err;
+
+       err = FsInit();
+       if (err) {
+               if (err == FFS_MEMORYERR)
+                       return -ENOMEM;
+               else
+                       return -EIO;
+       }
+
+       printk(KERN_INFO "exFAT: Version %s\n", EXFAT_VERSION);
+
+       err = exfat_init_inodecache();
+       if (err)
+               goto out;
+
+       err = register_filesystem(&exfat_fs_type);
+       if (err)
+               goto out;
+
+       return 0;
+out:
+       FsShutdown();
+       return err;
+}
+
+static void __exit exit_exfat(void)
+{
+       exfat_destroy_inodecache();
+       unregister_filesystem(&exfat_fs_type);
+       FsShutdown();
+}
+
+module_init(init_exfat);
+module_exit(exit_exfat);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("exFAT Filesystem Driver");
+#ifdef MODULE_ALIAS_FS
+#if defined(CONFIG_MACH_LGE) || defined(CONFIG_HTC_BATT_CORE)
+MODULE_ALIAS_FS("texfat");
+#else
+MODULE_ALIAS_FS("exfat");
+#endif
+#endif
diff --git a/fs/exfat/exfat_super.h b/fs/exfat/exfat_super.h
new file mode 100644 (file)
index 0000000..916811e
--- /dev/null
@@ -0,0 +1,171 @@
+/* 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_data.h"
+#include "exfat_oal.h"
+
+#include "exfat_blkdev.h"
+#include "exfat_cache.h"
+#include "exfat_nls.h"
+#include "exfat_api.h"
+#include "exfat_core.h"
+
+#define EXFAT_ERRORS_CONT  1    /* ignore error and continue */
+#define EXFAT_ERRORS_PANIC 2    /* panic on error */
+#define EXFAT_ERRORS_RO    3    /* remount r/o on error */
+
+/* ioctl command */
+#define EXFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32)
+
+struct exfat_mount_options {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+       kuid_t fs_uid;
+       kgid_t fs_gid;
+#else
+       uid_t fs_uid;
+       gid_t fs_gid;
+#endif
+       unsigned short fs_fmask;
+       unsigned short fs_dmask;
+       unsigned short allow_utime; /* permission for setting the [am]time */
+       unsigned short codepage;    /* codepage for shortname conversions */
+       char *iocharset;            /* charset for filename input/display */
+       unsigned char casesensitive;
+       unsigned char errors;       /* on error: continue, panic, remount-ro */
+#ifdef CONFIG_EXFAT_DISCARD
+       unsigned char discard;      /* flag on if -o dicard specified and device support discard() */
+#endif /* CONFIG_EXFAT_DISCARD */
+};
+
+#define EXFAT_HASH_BITS    8
+#define EXFAT_HASH_SIZE    (1UL << EXFAT_HASH_BITS)
+
+/*
+ * EXFAT file system in-core superblock data
+ */
+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; /* Codepage used on disk */
+       struct nls_table *nls_io;   /* Charset used for input and display */
+
+       struct inode *fat_inode;
+
+       spinlock_t inode_hash_lock;
+       struct hlist_head inode_hashtable[EXFAT_HASH_SIZE];
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+       long debug_flags;
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+};
+
+/*
+ * EXFAT file system inode data in memory
+ */
+struct exfat_inode_info {
+       FILE_ID_T fid;
+       char  *target;
+       /* NOTE: mmu_private is 64bits, so must hold ->i_mutex to access */
+       loff_t mmu_private;         /* physically allocated size */
+       loff_t i_pos;               /* on-disk position of directory entry or 0 */
+       struct hlist_node i_hash_fat;   /* hash by i_location */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00)
+       struct rw_semaphore truncate_lock;
+#endif
+       struct inode vfs_inode;
+       struct rw_semaphore i_alloc_sem; /* protect bmap against truncate */
+};
+
+#define EXFAT_SB(sb)           ((struct exfat_sb_info *)((sb)->s_fs_info))
+
+static inline struct exfat_inode_info *EXFAT_I(struct inode *inode)
+{
+       return container_of(inode, struct exfat_inode_info, vfs_inode);
+}
+
+/*
+ * If ->i_mode can't hold S_IWUGO (i.e. ATTR_RO), we use ->i_attrs to
+ * save ATTR_RO instead of ->i_mode.
+ *
+ * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only
+ * bit, it's just used as flag for app.
+ */
+static inline int exfat_mode_can_hold_ro(struct inode *inode)
+{
+       struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+
+       if (S_ISDIR(inode->i_mode))
+               return 0;
+
+       if ((~sbi->options.fs_fmask) & S_IWUGO)
+               return 1;
+       return 0;
+}
+
+/* Convert attribute bits and a mask to the UNIX mode. */
+static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi,
+                                                                        u32 attr, mode_t mode)
+{
+       if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR))
+               mode &= ~S_IWUGO;
+
+       if (attr & ATTR_SUBDIR)
+               return (mode & ~sbi->options.fs_dmask) | S_IFDIR;
+       else if (attr & ATTR_SYMLINK)
+               return (mode & ~sbi->options.fs_dmask) | S_IFLNK;
+       else
+               return (mode & ~sbi->options.fs_fmask) | S_IFREG;
+}
+
+/* Return the FAT attribute byte for this inode */
+static inline u32 exfat_make_attr(struct inode *inode)
+{
+       if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO))
+               return (EXFAT_I(inode)->fid.attr) | ATTR_READONLY;
+       else
+               return EXFAT_I(inode)->fid.attr;
+}
+
+static inline void exfat_save_attr(struct inode *inode, u32 attr)
+{
+       if (exfat_mode_can_hold_ro(inode))
+               EXFAT_I(inode)->fid.attr = attr & ATTR_RWMASK;
+       else
+               EXFAT_I(inode)->fid.attr = attr & (ATTR_RWMASK | ATTR_READONLY);
+}
+
+#endif /* _EXFAT_LINUX_H */
diff --git a/fs/exfat/exfat_upcase.c b/fs/exfat/exfat_upcase.c
new file mode 100644 (file)
index 0000000..3807f37
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ *  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.
+ */
+
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_upcase.c                                            */
+/*  PURPOSE : exFAT Up-case Table                                       */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY (Ver 0.9)                                          */
+/*                                                                      */
+/*  - 2010.11.15 [Joosun Hahn] : first writing                          */
+/*                                                                      */
+/************************************************************************/
+
+#include "exfat_config.h"
+
+#include "exfat_nls.h"
+
+const u8 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/fs/exfat/exfat_version.h b/fs/exfat/exfat_version.h
new file mode 100644 (file)
index 0000000..a93fa46
--- /dev/null
@@ -0,0 +1,19 @@
+/************************************************************************/
+/*                                                                      */
+/*  PROJECT : exFAT & FAT12/16/32 File System                           */
+/*  FILE    : exfat_version.h                                           */
+/*  PURPOSE : exFAT File Manager                                        */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  NOTES                                                               */
+/*                                                                      */
+/*----------------------------------------------------------------------*/
+/*  REVISION HISTORY                                                    */
+/*                                                                      */
+/*  - 2012.02.10 : Release Version 1.1.0                                */
+/*  - 2012.04.02 : P1 : Change Module License to Samsung Proprietary    */
+/*  - 2012.06.07 : P2 : Fixed incorrect filename problem                */
+/*                                                                      */
+/************************************************************************/
+
+#define EXFAT_VERSION  "1.2.9"