From a685df607b49263e099e2c7e96eb905a49ff847c Mon Sep 17 00:00:00 2001 From: payton Date: Thu, 23 Nov 2023 15:45:32 +0800 Subject: [PATCH] =?UTF-8?q?1.exfat=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../init.d/S07_SysInit | 2 + .../source/cardv/SrcCode/System/SysStrg_Exe.c | 112 +- code/driver/source/Makefile | 1 + code/driver/source/fs/exfat/.gitignore | 7 + code/driver/source/fs/exfat/Kconfig | 39 + code/driver/source/fs/exfat/LICENSE | 339 ++ code/driver/source/fs/exfat/Makefile | 37 + code/driver/source/fs/exfat/README.md | 98 + .../source/fs/exfat/Untitled Project.IAB | Bin 0 -> 94208 bytes .../source/fs/exfat/Untitled Project.IAD | Bin 0 -> 1248 bytes .../source/fs/exfat/Untitled Project.IMB | Bin 0 -> 24576 bytes .../source/fs/exfat/Untitled Project.IMD | Bin 0 -> 568 bytes .../source/fs/exfat/Untitled Project.PFI | Bin 0 -> 84 bytes .../source/fs/exfat/Untitled Project.PO | Bin 0 -> 776 bytes .../source/fs/exfat/Untitled Project.PR | Bin 0 -> 7064 bytes .../source/fs/exfat/Untitled Project.PRI | Bin 0 -> 43016 bytes .../source/fs/exfat/Untitled Project.PS | Bin 0 -> 201236 bytes .../fs/exfat/Untitled Project.SearchResults | 501 ++ .../source/fs/exfat/Untitled Project.WK3 | Bin 0 -> 90102 bytes code/driver/source/fs/exfat/dkms.conf | 7 + code/driver/source/fs/exfat/exfat-km.mk | 11 + code/driver/source/fs/exfat/exfat_api.c | 528 ++ code/driver/source/fs/exfat/exfat_api.h | 206 + code/driver/source/fs/exfat/exfat_bitmap.c | 63 + code/driver/source/fs/exfat/exfat_bitmap.h | 55 + code/driver/source/fs/exfat/exfat_blkdev.c | 197 + code/driver/source/fs/exfat/exfat_blkdev.h | 73 + code/driver/source/fs/exfat/exfat_cache.c | 784 +++ code/driver/source/fs/exfat/exfat_cache.h | 85 + code/driver/source/fs/exfat/exfat_config.h | 69 + code/driver/source/fs/exfat/exfat_core.c | 5138 +++++++++++++++++ code/driver/source/fs/exfat/exfat_core.h | 671 +++ code/driver/source/fs/exfat/exfat_data.c | 77 + code/driver/source/fs/exfat/exfat_data.h | 58 + code/driver/source/fs/exfat/exfat_nls.c | 448 ++ code/driver/source/fs/exfat/exfat_nls.h | 91 + code/driver/source/fs/exfat/exfat_oal.c | 196 + code/driver/source/fs/exfat/exfat_oal.h | 74 + code/driver/source/fs/exfat/exfat_super.c | 2753 +++++++++ code/driver/source/fs/exfat/exfat_super.h | 173 + code/driver/source/fs/exfat/exfat_upcase.c | 405 ++ code/driver/source/fs/exfat/exfat_version.h | 19 + code/lib/external/Makefile | 16 +- .../make_post.sh | 1 + .../source/cardv/SrcCode/System/SysStrg_Exe.c | 4 +- 45 files changed, 13328 insertions(+), 10 deletions(-) create mode 100755 code/driver/source/fs/exfat/.gitignore create mode 100755 code/driver/source/fs/exfat/Kconfig create mode 100755 code/driver/source/fs/exfat/LICENSE create mode 100755 code/driver/source/fs/exfat/Makefile create mode 100755 code/driver/source/fs/exfat/README.md create mode 100755 code/driver/source/fs/exfat/Untitled Project.IAB create mode 100755 code/driver/source/fs/exfat/Untitled Project.IAD create mode 100755 code/driver/source/fs/exfat/Untitled Project.IMB create mode 100755 code/driver/source/fs/exfat/Untitled Project.IMD create mode 100755 code/driver/source/fs/exfat/Untitled Project.PFI create mode 100755 code/driver/source/fs/exfat/Untitled Project.PO create mode 100755 code/driver/source/fs/exfat/Untitled Project.PR create mode 100755 code/driver/source/fs/exfat/Untitled Project.PRI create mode 100755 code/driver/source/fs/exfat/Untitled Project.PS create mode 100755 code/driver/source/fs/exfat/Untitled Project.SearchResults create mode 100755 code/driver/source/fs/exfat/Untitled Project.WK3 create mode 100755 code/driver/source/fs/exfat/dkms.conf create mode 100755 code/driver/source/fs/exfat/exfat-km.mk create mode 100755 code/driver/source/fs/exfat/exfat_api.c create mode 100755 code/driver/source/fs/exfat/exfat_api.h create mode 100755 code/driver/source/fs/exfat/exfat_bitmap.c create mode 100755 code/driver/source/fs/exfat/exfat_bitmap.h create mode 100755 code/driver/source/fs/exfat/exfat_blkdev.c create mode 100755 code/driver/source/fs/exfat/exfat_blkdev.h create mode 100755 code/driver/source/fs/exfat/exfat_cache.c create mode 100755 code/driver/source/fs/exfat/exfat_cache.h create mode 100755 code/driver/source/fs/exfat/exfat_config.h create mode 100755 code/driver/source/fs/exfat/exfat_core.c create mode 100755 code/driver/source/fs/exfat/exfat_core.h create mode 100755 code/driver/source/fs/exfat/exfat_data.c create mode 100755 code/driver/source/fs/exfat/exfat_data.h create mode 100755 code/driver/source/fs/exfat/exfat_nls.c create mode 100755 code/driver/source/fs/exfat/exfat_nls.h create mode 100755 code/driver/source/fs/exfat/exfat_oal.c create mode 100755 code/driver/source/fs/exfat/exfat_oal.h create mode 100755 code/driver/source/fs/exfat/exfat_super.c create mode 100755 code/driver/source/fs/exfat/exfat_super.h create mode 100755 code/driver/source/fs/exfat/exfat_upcase.c create mode 100755 code/driver/source/fs/exfat/exfat_version.h diff --git a/BSP/root-fs/rootfs/etc_Model/etc_565_HUNTING_EVB_LINUX_4G_S550/init.d/S07_SysInit b/BSP/root-fs/rootfs/etc_Model/etc_565_HUNTING_EVB_LINUX_4G_S550/init.d/S07_SysInit index fab776891..7a29d54bc 100755 --- a/BSP/root-fs/rootfs/etc_Model/etc_565_HUNTING_EVB_LINUX_4G_S550/init.d/S07_SysInit +++ b/BSP/root-fs/rootfs/etc_Model/etc_565_HUNTING_EVB_LINUX_4G_S550/init.d/S07_SysInit @@ -11,6 +11,8 @@ SF_ADC_MUXA=224 SF_ADC_MUXB=225 DELAY=0.003 +insmod /etc/lib/modules/$KERVER/extra/fs/exfat/exfat.ko + echo ${SF_ADC_MUXA} > /sys/class/gpio/export echo ${SF_ADC_MUXB} > /sys/class/gpio/export diff --git a/code/application/source/cardv/SrcCode/System/SysStrg_Exe.c b/code/application/source/cardv/SrcCode/System/SysStrg_Exe.c index 2c87e66f0..2b27918ab 100644 --- a/code/application/source/cardv/SrcCode/System/SysStrg_Exe.c +++ b/code/application/source/cardv/SrcCode/System/SysStrg_Exe.c @@ -131,6 +131,7 @@ static FST_FS_TYPE m_GxStrgType = FST_FS_TYPE_UITRON; #if (FWS_FUNC == ENABLE) static void *mp_fwsrv_work_buf = NULL; #endif +static BOOL g_bSupportExfat = FALSE; /////////////////////////////////////////////////////////////////////////////// // // EMBMEM @@ -359,6 +360,8 @@ void System_OnStrgInit_FS(void) GxStrg_SetConfigEx(0, FILE_CFG_MAX_OPEN_FILE, 10); #endif + GxStrg_SetConfigEx(0, FILE_CFG_SUPPORT_EXFAT, TRUE); + g_bSupportExfat = TRUE; //set the device node of msdc mode for emmc only #if (defined(_EMBMEM_EMMC_) && !defined(__FREERTOS)) emmc_set_dev_node("/dev/mmcblk2p5"); //This devicde node is related to storate-partition, it is last rootfslX logical partition. Using "cat /proc/nvt_info/emmc" to get. @@ -586,14 +589,64 @@ void Card_DetBusy(void) } #if (FSCK_FUNC == ENABLE) +int search_str_in_file(char *path, char *str) +{ + FILE *fp = NULL; + UINT32 u32ize = 0; + int found = 0; + char *pStrSrc = NULL; + + fp = fopen(path, "r"); + if (fp) { + fseek(fp, 0, SEEK_END); + u32ize = ftell(fp); // take file size + fseek(fp, 0, SEEK_SET); // move to position zero + pStrSrc = (char *)malloc(u32ize * sizeof(char)); + + if (pStrSrc) { + fread(pStrSrc, 1, u32ize, fp); + if (strstr(pStrSrc, str)) { + found = 1; + } + free(pStrSrc); + } + + fclose(fp); + fp = NULL; + pStrSrc = NULL; + u32ize = 0; + } + + return found; +} + +int System_check_mmcblk0p1(void) +{ + SysMain_system("ls /dev/mmcblk0p1 > /tmp/lsdev.txt"); + vos_util_delay_ms(100); + if (search_str_in_file("/tmp/lsdev.txt", "/dev/mmcblk0p1")) { + return 1; + } else { + return 0; + } +} + int System_mount_storage(char *pMountPath) { int ret = 0; time_t t, t_local, t_gmt; struct tm tt_local, tt_gmt; char opts[20]; - char *pDevSrc = "/dev/mmcblk0p1"; - char *pFileSysType = "vfat"; + char *pDevSrc; + char *pFileSysType = "vfat"; + char *pFileSysExType = "exfat"; + BOOL bexfat = FALSE; + + if (System_check_mmcblk0p1()) { + pDevSrc = "/dev/mmcblk0p1"; + } else { + pDevSrc = "/dev/mmcblk0"; + } time(&t); localtime_r(&t, &tt_local); @@ -604,8 +657,47 @@ int System_mount_storage(char *pMountPath) snprintf(opts, 19, "time_offset=%d", (t_local-t_gmt)/60); DBG_IND("gtime=%d, ltime=%d, diff=%d, m=%d, %s\r\n", t_gmt, t_local, (t_local-t_gmt), (t_local-t_gmt)/60, opts); + { + long long DevSize = 0; + int fd = 0; + long long size = 0; + + DevSize = 0;//set to zero first + + fd = open(pDevSrc, O_RDONLY); + if(fd < 0) { + DBG_ERR("open %s: errno = %d, errmsg = %s\r\n", pDevSrc, errno, strerror(errno)); + } else { + if(ioctl(fd, BLKGETSIZE64, &size) < 0) { + DBG_ERR("ioctl BLKGETSIZE64 failed\r\n"); + } + + if (0 != close(fd)) { + DBG_ERR("close %s: errno = %d, errmsg = %s\r\n", pDevSrc, errno, strerror(errno)); + } + } + + DBG_DUMP("%s: %s size = %lld\r\n", __func__, pDevSrc, size); + + DevSize = size; + + //original SD card + if(g_bSupportExfat && DevSize > 32*1024*1024*1024LL) + { + bexfat = TRUE; + } + else + { + bexfat = FALSE; + } + } + // mount sd card - ret = mount(pDevSrc, pMountPath, pFileSysType, MS_DIRSYNC, opts); + if (bexfat == FALSE) { + ret = mount(pDevSrc, pMountPath, pFileSysType, MS_DIRSYNC, opts); + } else { + ret = mount(pDevSrc, pMountPath, pFileSysExType, MS_DIRSYNC, opts); + } if(ret) { if (errno == EBUSY) { @@ -636,6 +728,7 @@ int System_umount_storage(char *pMountPath) } #endif + INT32 System_OnStrgInsert(VControl *pCtrl, UINT32 paramNum, UINT32 *paramArray) { UINT32 stg_id = paramArray[0]; @@ -660,8 +753,15 @@ INT32 System_OnStrgInsert(VControl *pCtrl, UINT32 paramNum, UINT32 *paramArray) System_umount_storage(pMountPath); /*Under normal situation, fsck checking doesn't need to print the message via UART port. We sotre the message to /tmp/fsck.txt (on DRAM). */ - SysMain_system("fsck.fat -a /dev/mmcblk0p1 > /tmp/fsck.txt"); //Store to /tmp/fsck.txt and use "cat /tmp/fsck.txt". - //SysMain_system("fsck.fat -a /dev/mmcblk0p1"); //The fsck ckecking will print the message directly. + if (System_check_mmcblk0p1()) { + DBG_IND("fsck /dev/mmcblk0p1\r\n"); + SysMain_system("fsck.fat -a /dev/mmcblk0p1 > /tmp/fsck.txt"); //Store to /tmp/fsck.txt and use "cat /tmp/fsck.txt". + //SysMain_system("fsck.fat -a /dev/mmcblk0p1"); //The fsck checking will print the message directly. + } else { + DBG_IND("no /dev/mmcblk0p1, try to fsck /dev/mmcblk0\r\n"); + SysMain_system("fsck.fat -a /dev/mmcblk0 > /tmp/fsck.txt"); //Store to /tmp/fsck.txt and use "cat /tmp/fsck.txt". + //SysMain_system("fsck.fat -a /dev/mmcblk0"); //The fsck checking will print the message directly. + } ret_val = System_mount_storage(pMountPath); if (ret_val) { @@ -722,7 +822,7 @@ INT32 System_OnStrgInsert(VControl *pCtrl, UINT32 paramNum, UINT32 *paramArray) dcfParm.WorkbuffSize = POOL_SIZE_DCF_BUFFER; DCF_Open(&dcfParm); -// DCF_ScanObj(); + // DCF_ScanObj(); } #endif diff --git a/code/driver/source/Makefile b/code/driver/source/Makefile index 8aff395ef..ab237b184 100755 --- a/code/driver/source/Makefile +++ b/code/driver/source/Makefile @@ -46,6 +46,7 @@ obj-m += \ msdcnvt/msdcnvt_custom_si/ \ touch/touch_gt911/ \ mcu/ \ + fs/exfat/ \ #obj-m += net/bcmdhd.100.10.545.x/ \ obj-$(CONFIG_HAVE_HW_BREAKPOINT) += debug/nvt_data_breakpoint/ diff --git a/code/driver/source/fs/exfat/.gitignore b/code/driver/source/fs/exfat/.gitignore new file mode 100755 index 000000000..241505f47 --- /dev/null +++ b/code/driver/source/fs/exfat/.gitignore @@ -0,0 +1,7 @@ +*.cmd +*.ko +*.mod.c +modules.order +Module.symvers +*.o +.tmp_versions diff --git a/code/driver/source/fs/exfat/Kconfig b/code/driver/source/fs/exfat/Kconfig new file mode 100755 index 000000000..78b32aa2c --- /dev/null +++ b/code/driver/source/fs/exfat/Kconfig @@ -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/code/driver/source/fs/exfat/LICENSE b/code/driver/source/fs/exfat/LICENSE new file mode 100755 index 000000000..d159169d1 --- /dev/null +++ b/code/driver/source/fs/exfat/LICENSE @@ -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. + + + Copyright (C) + + 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. + + , 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/code/driver/source/fs/exfat/Makefile b/code/driver/source/fs/exfat/Makefile new file mode 100755 index 000000000..52bcee4b9 --- /dev/null +++ b/code/driver/source/fs/exfat/Makefile @@ -0,0 +1,37 @@ +obj-m += 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 +ccflags-y += -I$(NVT_DRIVER_DIR)/include + +ifeq ($(KERNELRELEASE),) +PWD := $(shell pwd) +KERVER ?= $(NVT_LINUX_VER) +KDIR ?= $(KERNELDIR) +MDIR ?= $(KERNELDIR)/_install_modules/lib/modules/$(KERVER)/extra +MODPATH := $(shell echo $(PWD) | awk -F'linux-driver/' '{print $$NF}') +MODNAME := $(shell echo $(obj-m:.o=.ko)) + +modules: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +modules_install: + @if [ -z $(NVT_MOD_INSTALL) ]; then \ + rm -f $(MDIR)/$(MODPATH)/$(MODNAME); \ + install -m644 -b -D $(MODNAME) ${MDIR}/$(MODPATH)/$(MODNAME); \ + cd $(KDIR)/_install_modules/lib/modules/$(KERVER)/; depmod -b $(KDIR)/_install_modules/ -a $(KERVER); \ + else \ + mkdir -p $(NVT_MOD_INSTALL)/lib/modules/$(KERVER); \ + install -m644 -b -D $(MODNAME) $(NVT_MOD_INSTALL)/lib/modules/$(KERVER)/extra/$(MODPATH)/$(MODNAME); \ + fi + +clean: + @SRC="`find . -name "*.c" ! -name "*.mod.c"`"; \ + if [ "$$SRC" ]; then \ + rm -rf .tmp_versions Module.symvers modules.order `find . -type f -name "*.mod.c" -o -name ".*.cmd" -o -name "*.o" -o -name "*.ko" -o -name "modules.order" -o -name *~`; \ + echo ">>> Clean"; \ + else \ + echo ">>> Skip"; \ + fi \ + +.PHONY: modules modules_install clean +endif diff --git a/code/driver/source/fs/exfat/README.md b/code/driver/source/fs/exfat/README.md new file mode 100755 index 000000000..feab40038 --- /dev/null +++ b/code/driver/source/fs/exfat/README.md @@ -0,0 +1,98 @@ +exfat-nofuse +============ + +Linux non-fuse read/write kernel driver for the exFAT, FAT12, FAT16 and vfat (FAT32) file systems.
+Originally ported from Android kernel v3.0. + +Kudos to ksv1986 for the mutex patch!
+Thanks to JackNorris for being awesome and providing the clear_inode() patch.
+
+Big thanks to lqs for completing the driver!
+Big thanks to benpicco for fixing 3.11.y compatibility! + + +Special thanks to github user AndreiLux for spreading the word about the leak!
+ + +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/code/driver/source/fs/exfat/Untitled Project.IAB b/code/driver/source/fs/exfat/Untitled Project.IAB new file mode 100755 index 0000000000000000000000000000000000000000..9b2f9be2d6e4605dfa88550caaf872f3cc82284f GIT binary patch literal 94208 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'zJ{Fn8hnhn*AQUl zYp_b|zq$tM8mMcau7SD+>KgdZ)d1_EjJK?Z!rzYbSD5vEnETH#>;3S9&Oi3G$;F;} z$k|t{m*JoFGWdHgJl4;UvwjA%z5%no0YB=(V|@cT>l^ToU3yvXK+bvx{DgD=q~oVt z{H%}QpY;)#^%0o$5t#K6nDr6(=Pv%YX7;Dm{;$OSadR*HPRCzz{9W1Kn_8hJWbfk2?R4X?*gYA?`U34FA~tr}ic986baL z8|s`^+%+FJbP>z|Xk+az6z*=aXU18^b@d^iHj&hx5kBId2Se9s%Y&1I&2_ znDYYgZ=HUAC#+Qx`R`qRo_Fs5;P{WmX?ecj{J-euXNL_fHEiP5-|vytID(TR47iz~X2ycr1At{0zWBB(knbRWOl#Vw>V8MzsbQxo?<71!_J?pk-*}fQ?;PJ} zM81ps(J1?U@UHqtrhIl2)+sjf-Q|y&+Ze=l>N_K$lD zPnYv)>P+>~?84v2`QO)tzn{pPW~B9Vf8oZaJ*wdyVE)tm9O#(uQ|Lcy{G3OC50XE! z_2Xa{{~;z%`_Dt2e38lR{c8(?{ML!8TLM-$yr}hXF49Sz6PHe*Vo{@ z<8_V))FJwp@CKWt`peJ!OZI)?pR(_k&ZWOR_gJB8kWjl!!lgeu*6ftQyt4<;#Ti zy(Hwl#@p(75eu(hcxrf6Rep*2xASo(XTF4g<{S8DK7srfalSzOze>*=RB~(j!f#Uh z-uO?^J^rQlGyM=g`;ExiubUmy|BcM~Vbu@c2a)|sOF!QSf%!fN%>7dfPtwQzQ+Pq# zAA-4m3h(9QdpmA+ypQ939WRND{x}Z?vwwDM%pdz@$PbMB5t2R`pZap|tC>Ib<)|M` zDnGY8eu9*@o*#lKKbZY#82#G*(7ENc=oh9uVDt@3wEs)LmUMrmvaBVvFRkU!>iBn= z`X%nWp0DNNA059Yb3RktcOT{WZK?h={_u9k?{JJgBK0*a%MXv-nx2oVEXzy%OxwGJ z*B5>)?)3bU=-2osPX4%QEBQW$x_`pSc`qLSPdPd7#Up>l;!D2gq2WC%tSUkNoQvi*}#e9uF}`-L#y?@<1w3-4DhykCoaD|rur_-Cc-4W^H+9M5(<$MIap zTRU!@qIyP@QK3uJ}rDw zx<6+AJ4CMM(UErwYmW_gIo1I*+Kc9YrIT|$5C1(P*YkNW{Xg6%a=x#k^^@@c`6?Iw zYGbq4E&ese_9%lf=kxG?s>rMLTf#q0SkLDnzeTtyd0)=_zmP32asQ&@mmD*G5Z<4i z{4b7QcKlbze{;-v8^ZsGlXKn%Ip=L)&fCDRIRBy?>;cO^=WTG$_y=>|2IjmC%y}D_ z^ENQ&ZD7vZztE+1*tx2CEK47;ytKrSe^p7g% zej(|TYFz)-HBi?;T?2Iu{1?;!??uz!&vxU%9LIAVZ|#`x2@u{qC*Ribc8<4q%zbvk z+tJB)ay;Mh&W?9+ysP8g9PjR!{ZCz=S~e8&?0>@SCzw4sJQ!fH_bTg!)vk@-bEzyF z9x3ePco)YKj*R#7Z2eH(FL3f^VYL`+z98~_g|hC~sJS6O)!(xAjvVim_8yGA24kPWmr8uPfw!`p`=$3etbXsGS?|^Qet_cx9lzS~LdOR=KG^Xgjt_Ob$nj#wtVdh^CB26^ z`QeU_aLjoN^v8J%nDZ7e=Ph97kHSIq#rcaUAL0B3{LPy7T^#(D_~ZWivf<(&>#s@q z7YA8?g%?Ww)c0bN@QQ=1=fPtl*ZcHVUnIPm>bmagg&SgooVb?-JhMV*g?J8_92gt-lvuB zT*=>5*&jpR6ZH-Ao~&p5vmeqN!~b`hAB&IoGw~1OKOggh97fLmFyWt?U0>)7xzlR7 z|D^OjQCKGLr?CHR{)IV@B=sdMD~*h>9%J0y(|KFer=+K{Y`jo7Nc5xMUlX}8>)G&x z$eHh}`*L=DTX<6BdZfwPgYfsV?LnCJSHjynTi-?A?D#dQzAQb<9CN;k@W_wqQ`3ik z)2}f8;lm}pdf#94CE=Gw*)L1hNb`Y-HZoUCt&yv+Nb z(f`l_k!SKFmXH2Y|9ctR`7BM(_}<28c+JMu{DysMxoVG+{{5W%^f;bGc?b3>?$4-| zi#b~V)iqGpz<-to*4DPK(wVhf&ew8bT`f;AU!=Vb*2)XaACa$j@(s0I9;)Rb=9~CG zt5#mvSj*!hwOnR?ivMD*yuf@F`IwU*9LHN5?lwDk#mG+ioFxV zD`EdcE^iijW<1z%p0FO_LC$^S$bI|A_xO!_L+QX;Ze7?Q-M{>zyqoX6M-F`NJ#wD! zy+7$GKV#%WAp2yq2rqUnafn zPJUu7Pn_f!dq4Ug>#UV;=&I$=UCV`)wLEfi);^T_js0%ni#^d>E8oyp%jG9xcxF$D zee&nXv3zpJXXo#|p?E^HH?4gMw>aj!7V=gn=luob%-3M-QJD7_VBTYZnIFNt#{lyl z1I&92F!m$d<&v4)CXA?(cWOr1DO8$J31KOth^_S1B`F5jxF_HT?2Iu)HP7oKwSfM4gA;A0Q1Yl9_VVt zJ~4Yt>;dd^vj_USTCvYz>~k3V9L7F}vCm=ba~S&^#y*F!&tdFy82cQ?K8LZ-VeE4l z`y9qThq2FL>~k3V9L7F}vCm=b^D4*nXq{gz&xL_iwH#JEUgP)_$EP|z&GG4uU+0+f z^HM*;@y7G z&$vHt?sI*C{d#zBCvSFqh^@!irjCVosAKl;Nbh1NUm~pU8zVnVSl>5>+3$cmoqz5h zAZI@iUg_MQEUd5fBIo`A%zg>X`B#|pukiU29{WeC{|kim`x#dMgfA41_ti~57YWZu z?oS)DAA|evXZD{oyhj|fpGo{bbn-_XKjxVI9{fMy%9Hy9$hl7dvtJ5xp8)1Q0sM-K zUzBhAN60xZ1#?~sW`8MZ&wXL7PQ*mx=BIj^Yp!V^x;eR%8#?!&{}hll^3o*y*( z;~$RMpH_QD<70mpUKjUc;K_JDHL-6dhPb~9Uli}B8n^VWxWw@XU3kB3)C?!{KaF4h ztlIa=Q<_w+H`0^zH3-M=msoo9@%z zm#^61nDh9Szu{t;^Y}3P?N%Nl$GxRL;~vI6%=<`KU6{X2>Slm~XKb0-- z?*5){#r#|RB8S!ekZ^A-FO!S=`?LC(+)((bWA0OEdAGNP9QPa4zcTk5VD2}-Z_m_! zb zukX`Y{)JgTu=EteIO_)%o*cH>BI^bEy|KvE?~TEnABkK(GtO@bkNGg+732DpaaZX3 zVsuY<4`<4&y%@^Bar|=R=zid?On)NoCmwKoSY|%j-ql*2zf1Z;dvUP9{2QjdX!~jJ zYRl1H;Iq^Dn#oxoOVYExbfg{SN$$(EcP;B?y$t!Jlb_@G^{Q_9nf}E88?xmue1+qi zo%?q?`FkAS;@od>@>?C>=J$*WKC5q%{;x#$@K+sw&GFYA-{Y9|F!cLw zsjpL{b|>|3eTnrP(PT1#{jC=DZcmc`KOnRxszS z;1w=@&RZepycNuOE12_EF#Rjcc`KOnRxszS;KBHw9rcg&3`KAd@1sZ$Tc<@mMy)P2o(m z|4R8M>H4RI_qgLJal9q|2FG(9bAAx_Ls3q8h8>^fc%$PH$L!Z7{qt~{{W|zF8T(%I z_vQHhFX_RaPwK;Pd8#B#-)ln7`=q4z2>C-x2tF=rA4&M6M+f0yVbTL1AMI<4ud6$J z*X;ipI^KvqQ5t+SrVoBhrJzqy|! z8P)Z9(Wf!*-{O8jdS1}vdzyZd?;~n{crO)|J2{{u50haXD! z!^FN^)pBQzy}7DIvRU)vJs``kxbMi!|J464rGDx6&L#Xxdv_?%--J_FuQX6*e;J-`^=+EA*U&C<<}b$G zt$nYT{%vmZzH+5q?k|mI`wwB}6B10Ny*HFU7QZ(Zx#h^L{V(6=dX2@G?B7XvJ?vLm ze5-Oj%opHQ(La28I^PWK;-30#`CXeIp?)Vkl&5|tJXU&LoX?8BDkqMP1z6>0jgGC| zF;o60<$AiFlJetz?n%QVBVWkWhmMw|xksY?(=7fw^6Lt}cRZHHXL4BM8xi-+SH*v& zBR^4{|HEgC{`LN@Z(cUOf&2t)@d;Bt=04Z5^5dEB!*vLMBF_Jj zQ-1K#X?axC{Rx@%8s&i)zooxW4CEL7kdyD)B#Mw6YWwIA_qz$}l@a)uw7(A>xvu5M zIv#gicKlK-4{aY3{)Sf^AD@mtp`*Pu$Nf`_f4I1j{a2Xxr9~g&zN`P>7~Von|L{ba z`3a1@4`c7ctjB46RR5b|`-WMcgD-UQouWQ1Jn2u~AoXQVI3kD|D;=f$;1$9%n+~q> z8--`c`@6{Br0!=V?{%sBQl@aBZOlt* z^*ypAeWi_C*QBqsafU>!@BJfZeZkVxy*m6R9WN>!HRv(0)ck(}&d|Fjb zeX#t9d&(cCya|u;g-^)T=gyYp?X*`-UuVlnIoc!4d*_m$N@sp!f%nc~?#m`T7V@dS z#Qj)UC;HO&+K_LP>dWNJKQ(>op7|$?T>G2R{McIL@I7(;K=a?3pV-Lyj`?rtJ?ZS2 z-y}b40__`?D=^bk#cSySb@d8%&%Ad}xuXeU~g?nTBwfqkkt1Rv-qr!dZcvn&Pm(+w`oP?9` zi<2`Y40k-syPOjzrmz8&STaNQzFz3Hu&VRxCWbBnr@z3`q zEI*~eYW;&*k2e1zzdDQNLCm z;(t5GhdN&5c(G&BYx%n1_5DY}d#a}VCX2s!{0GPL(*DT8=X(u=$M+gwzSjWry#|=?HNbqY z0p>mu%zY%7^F=V{i(u{>!Q3~3vEO0d12yjM@7l%czm0E|&eEpBK&pRZ#wRInW5y@= zchNokdkK&Ew}v;U=YW(uv!-0+uaZG!`{k(L3RO#$p9mH#t5h zUEc_u{e8J}9e>0z_NnTxtEDr?cm-dRwy#Q8ezX8f`!c>R%9|taF6o^u-|td=seAfg z?Y}jA&chmu{CIJ%-&4T-3BvkbKH@~JKBFE##mIQcD(w>ZAl`M=%C?{NIym|xSk z#P`0Ke$S*AIr@8lbdMay{|8+7cRKel?qR~`JP6^#xM#kP97f)mp4Sap-`|qH7h!yI z#=cg4lYWUmX#Jr4VB&`jHi&%OK=a&B&`)5mUhQ)uSD9rt7+`|^W z$XWlj@Yhd-D`h=tPFNC}w7rZ>gsX&g#DcFDo)z~WRDO+c{9cv0zgBpvya0#$>x6d? z166*raD$8p$lqd|>`!QT?~L`!($mo!u8-px?%(D32FEu#zRB^;j^FK={U_6xq=)rT z7`f?hWMVCH_@T^tOP8#7{L=AXBR2=pugE_U^K1F*4m~Ht`BdcG!fgGL_r-c@xKCb? zPv)DVuh7-q*V$Lg-PdNv_sP+4yJPm-t^SC7k2wCC{)P7w*6(>B=ldQo;|K0Po!Y~d zF7ba~c6<2dP=9Y5gsLC4>5{FLLT z9Y5pvS;x;g{;A`iIsUohUpW3tWc0Obc07^%FUj_w!rbq(`Y8OKY<=wStkxH;-%_5f z)ThW&{p!EE2I?B9YoM-yx(4bR_^B&D z48|UUvCm-aBN%%P#$JN4-(c(~7<&%Jo`SLOVC*Xx`xwR^g|P==>@oODnfXk&oW5Ya zLGAnQTxVduul8woVYJHXzf>4q%lYtyDqp`oaDG?Z2jK}=-7CMC+JnY_ax9azd0I#D zzgub_ntRydm+&|brS^7raiH)q@jE5HSKmEWD1J7x-mCo8bUqrgY~hKV^GX)~SYiCv zne!sze*A@W|5*Kxzv$$gPs0C?Qumeav5D|p>fZSGj$7h<-t<3K4oe-kI&O2EbG*#) za>pwiw>v)3@kx$59J3xl`n#OG+cEcraX(-Fm?86XnIDLL%flxN@6mLIt``}zUxA$c z3Yh&0nEeWv{R)`<3Yh&0nEeWv{R)`<3Yh&0nEeWv{R;TXcs?|#FXdr4sgLF1x5@l= zo_s%3+*i8CL*df6{s-@wZ7<`41=#!xe=uD?3@cmO+9+R*e`UTnyf)n*6?s=X>v7ti zR<`s6*4JRx*I>@8Yx%6qj~D(R>m|Blp!rdz{G=F_C%8YL+{cLNDIc4zH%NSaE7<=|!s}a6cJjAc`16wVUq)%zCc!1+hFV|H7wc)?-zES}adXpYZ99 zU+0+q8TV_Qob;)Z$JULnC4I^g-&)S=OL#eZc?_?B2dEdHMM zaGT@d7{2H~tQ;TS#Qh8_kMel+JdM?_$%&yqh&xpQ%zh%wej>b;{Gsb?KQxA{EQDD7mZAn z^Cs};+<5YwX>58`{!=IanaK6~_4uEi+3&IONbEcPJ}z>;j|Ox8MaN%t|8{ZD`Elj# zGWMYI_KrCpga6B%d}|qBL$LE+7XEDEx${rb^aKs>pp3oJlWS?4WA3L`<#QdMTH}6s z{~j8Aa(_s|8!7O8gCzWs!XqM$Ki|tE{Od#>zyD?O9n$hO-qGa6!RC? z$cy=TBADBhoVV^N6!YxoC+;T-UrFt2i7#cfKgWexPihuc`FEXr823Mpa@@221pmtU zhw*=QoNuUIsPVzb{}%lx`kyGk$eWyh82MpN4kJI>$zkM!P7WjgyGtL8e5&&gBR|~9 zVdTd+IgGsEIwJ7kGQ&reyt0 z%5zhG2jOW=`y}$w!C}^eq&zDx(;d1wpWK|t2aiwtCy{seabG-h~y;@JnUZKXK3cC;aQQewzP(xbWz22@m(CKZy_bFzJDp$ME1m{Ugzr#CJ~k zbh^GG`CA>nlQ~b>vpRGioaR6DtnO^#{S0d_=akmN7QQg&zqLGiWqs!2ChZUH{C1@` zzo~G&^dCAxioDXhE`+C~Kh^tHI=>Vi;eA8RugbYE7Tx!(mhecw$}5@sNWDu#3-_&~ z`#krnVeVJM+)spGALpkq_YGn0qrjS)`me5mx(4bRsB56Efw~6%duf34s@MaZSB2RR zgRgS-1@~EzzdrMRd~Yt4-%!ip3de7B{3gd&I=;&B)sC-m%>JF4UA=|;IQw@n?_rt! zRu~BX&g>8LW?1aQ%7ifY4V%@z%e96rnf(Ff+a2E*=MQENihr2!Dlt9@PtrF~x-7EV zr&$*F#_Z=v`ZfMu_H$tNbKoy#_J7p<-HzGs!9DvuF#A35DVhBt4ewOP=rg9b_iL#> zHT~tUJHE&9g<@ap`-(CDW!8hFewlynL&2;MN%~Cw4)L$64KVwKqW>W2?b|2r2bzCj z-qUK1{&^2;VdSIY`~znHj`T40!N!{BWNOX8+Py!ec#) z{9T@zZ>oN;i{az``|145^z&F`()+mM-#PdHifrkX@OO{(8UL`B_k_rgOz&fuzF(8p ze`D?!kbl+-VCLuWvCjW%9UteI{apN?=H%?>B47%~jA4Weg>4ym)Mt?Bz!Sp{c?E$8~!(Wg65lsDu>964ZLpWTDO8c8i zubj_X8TBFgHRgR6(UO)(cRVH5_eB5W z!+UDt;(4rI`ToOM>H4jtryM?D^5p#z<-5}LS(BT%{-3%A>KdqPpsstAbAPIE zgS>}<{50Wd@jW@USB8b>gk!7nvxH}aqpN(b@YJwlm3f~$vCqnZ{c`x7;$H2g#GWe$ z_Iu!)MLtF1Ul`N7MOfcAhdB=m-)7-8Ci_E@-g~BK(8+n*UirS&lX1Vw;un58-Oso3 zc*gOwj-PY>#Dk#v@g3Z&ZlfXGE^Fe)qSrp_YJH*4wcT?OFbw1yCR<~Z%pSa#-DKV&pY`K zoqTFq|C8{nz1sV}#+zgNG<`{YJIDG1_d5PfY_BG7U*2^=y8j${g};%Vj}31q{=_ls z38oK`!=^uB);o~H$XWkD4kKqh1UZcS>^Psa`qX}E?t*mvtYndYNP23R@VQ@r9LE2a7(VfR&Bgb1$IQM%e&Gm`nuck4`cnpJ^eBKFDIuzMSf_kAMhf_U;Q62`eQu<{ji<^vwlJN z)3W`G=mRFa9h@9Seqxj_jO9uH0B>;qVf>#J<)rVTxE^ik6a8KsnfiH&lV9rO>oVtG zw0vHS{ZEsoZ#0CL9DhdoQ?+-Hf3}v(pEFM0GZFvQ_rh`imCSn}D*vkEozwTTE4`($ ziN)FS8y^_r`z(^*&|A)rv;GXT{w(@2IqT0b>&>`ly;sr~diz>>zLv&k{->wqVZ5W` zogDAwc*rsDB}(~NeB?*UH%v%=h#w|?nDAl3he;ny`e5!mSp6vH%lk>ZIt0L+XNAY& z_yC_F{lV7qJ}8X62+xlFJ&gSV?;?tBOzd0LXZfwM|CfKR^!D|Hw>f^h<99gzP-GiV z`g(G_Cv03EAKpy@*7KZjbL@Ytz4Y~TUT*zcavrz0JU%*-j#m}sH)rlYN%}g^%-nZU z&d2hV@HPFN>m2j`5OU7%!JL-mJnY=lKOjFOyZ+g~w2k*gnx)ixC)W+G zu93I4o>0RpbAOihJZt_kEpKr@R`{!MquBQ__if-qGw<1}`$eXI``)zqKT5c2pNRZ4 z$0D@%$0Yrs_mob3KC*_T9@nsjR{zyC@PA7K(j02)qg3Y_p6Ym-odyW5X9Uq$QFHY&~IL+~0+5TDNyNiNmZgWi1A8#HV;l8=G zcj3LQAd~Nh^lmN=jHK~Zls_r*hU9*L@fV!@QIjX{rKbEjNdxmD=)2ae?)k`#{ChYKizK^_eX?Pj#oS8e2nTt-E%$$zQVc3 zen`@DM8F=j{45ULt=aS~SsebDO`q_ZY={d+xIZl$ z-Vuj|(;dIgG3R+KJaK=ulXIR2`J~iWHMw=X?#r(mxk6aK51{o$nePK2=lcL~bF9Dc znB%k3`@<66_y$<(lbE*~?v(%6^C{xL(pQ)$e>k>Z_y$SOw6Gv(`%wAynf9Xm^R#?I z--@0b_0Q5HO#Ol>PgpjaCFo@Ru&=!{ccC_4`C0tJ7sdH7e6iz-<4YW0>i9Cpmpfh} z`E88fckCM;EwCPJ^;7s{kvB-{VAgZtM>F$d_0Rex@_#!y>yOB{j{QZm>RbG8<9IvA z2RJ^^@xhJ{alFX!V#g;%UKrEEdKvtk%=bz(y{zw%pS`4X_4|WX-$lN+@O1fp4g994 zf5~r9|5rJ_+VR(HDw|XqtuNn5?f;uFQO=uQQC^ zP|LX+Yq{kMVvtM^hiN6R^o@paPiB5Coa4Q4(?=;3ekN6XIPa-OF7kdVe4Y5$_fz5PYr`MEp_Yp`IlkHP^Aet>IEI)1gX89OeIxXh zHWnU^>kSLVSKoMkfb%@C)>Bof=vU;EMJdub`Y%-$ea=hni<*4b*k2;wM=ApIE%m=| zYG0W9{Y2h0BYBTRJ1>p7O5o3riZl$ANvPW@NcKwSfM4g5zm z@NU#jeB89%-m<;`lpNPquB$4@%`gX0;|exm<}3I7Nuhmm(gxs1OSKl`)r8s{Iz zKkKo?4Q%e<$j`DeZ_<9Vg;yilF*!^g$^OMIcfP%M8eU9S%PxvsvA8GBWE zB*riPO+MnAGb`x@nOI()61D&Ztk*kA(O7Sibnj-qre|@nP@6 z*t@vL-i2?8`*}_3zpFiLaeQk!e=+&(PJV~u52W?Q+~1kj2jdSq{*dEqqJ3-Wk^D4A zxs_*ecwisL`#L_w@x=dtrM-kn2@m%$@xjCg6CZq~*ys8^hM2x0^9A@Ck?Z#tMBhQm zr#gRtnLohHAMk%eoFBk9M#eq!37Gi={KeYxS#!5z?i0oGU&DPOnE9{NPfh>1`-`Bl zX^)tnk>SVW-}&BJe{rNVJ+1HJex$_yjCeEVK3I~U$%#v2eTNwjEd5>my{tDGj}7Em zZ?g0Vv)+W9^(L713A6qLv;G7#{ur-pZFx`B7fk)c{Y}z8==zK0Z)`Bn`84?5BG>f< z_!BX|@FyL=S?Y^^e-UtI%r4b(MI*FaqZbq)M)YT(2;o-^LI zMtikc$8#Bf8PC<;RK~s8pTgbg{H@YoUN`co%zU}OZ)wM;9rNC}*cak{{cCt{-R!CI z@F?$zOZ=f<_^gcmyedC33~T(Ggr7+5k+7;TKJn)?J;v9j_tV3wTu<*vx;|W4CEqiH zExh#;Yj==v)#OlptSXJjTZPJYUdVWFVZGsiocSSqMw}nQ>l}|bW|7g^Y$c?94?VYCeCrJ7xM`Qcb`IWk-JgvQzM%GfEF!_fm zUl{$v=ody`F#3WiFPQRx(I-rOf+=5^@`I^w@XATmT>Y2@x$Kj90-J?9Np50{6S-)VhOUYDuQ%FO4G?{IQ69fM_V-%;r^tIZ zu+%@}yW;vC@{h&y2-@GAl56StxZ_X6^#|nlXZjlrAJ*_U3qRn(KQi7|!vE2ZPsp6# c*Z4KJ^VlJaE7FaqjQkd(V0I?e&Qa%5Yjz@}Lo7 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 MJkaEU|D*^01^Pl0{{R30 literal 0 HcmV?d00001 diff --git a/code/driver/source/fs/exfat/Untitled Project.IMD b/code/driver/source/fs/exfat/Untitled Project.IMD new file mode 100755 index 0000000000000000000000000000000000000000..38b402ec3d2745249fad2bdc5f83e068c86264d6 GIT binary patch literal 568 zcmbOv!oVQQV8qP8$S|Nl0BFAq(||ewY&jc{1Yr#z{sMH91=x=VnW1@yDLQ9agdAwP@Mrn-8v?Oy&;hSt{|Bn5a9q+=Kx|rKqmu(AdFp|n3oS? hF9i_+Ky?8i1_HcbW?*0eazSCCaqJmWgV}4aUjYuN8@~Vm literal 0 HcmV?d00001 diff --git a/code/driver/source/fs/exfat/Untitled Project.PFI b/code/driver/source/fs/exfat/Untitled Project.PFI new file mode 100755 index 0000000000000000000000000000000000000000..763d7e82d1c0cff399f73963647df7354376640d GIT binary patch literal 84 zcmWN|%ME}q48TxRX!#Wg&>=b$`_F-;JUKZ8U{Hc@gg&`v70p^8R=a6ebq;&#Qh49? I(W~am4?@@hhX4Qo literal 0 HcmV?d00001 diff --git a/code/driver/source/fs/exfat/Untitled Project.PO b/code/driver/source/fs/exfat/Untitled Project.PO new file mode 100755 index 0000000000000000000000000000000000000000..f1b04896f803c5b2f67c3ffb49354b4fbd8356ad GIT binary patch literal 776 zcmXpMXJC+JFkFMdG&M9ZHI2{7 z%qy*kFHXshFHO$LOo~a)Pf3kQDatHMEs815FD*(=jY%txNv%jrEI~JR$nY01F)(Bd t8Q0Rrp#>tcz)&C$Z~?WkK((Hbf6a7(`vnstl%E2mC465ny*T!a2>{S~Bf|gy literal 0 HcmV?d00001 diff --git a/code/driver/source/fs/exfat/Untitled Project.PR b/code/driver/source/fs/exfat/Untitled Project.PR new file mode 100755 index 0000000000000000000000000000000000000000..e77694cfaf7f281ef48e3520edf0867ffd3e875a GIT binary patch literal 7064 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 literal 0 HcmV?d00001 diff --git a/code/driver/source/fs/exfat/Untitled Project.PS b/code/driver/source/fs/exfat/Untitled Project.PS new file mode 100755 index 0000000000000000000000000000000000000000..369c24441a69d510f28333a5f3fceb04c2f53b7f GIT binary patch literal 201236 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 +Exfat_api.c: err = ffsLookupFile(inode, path, fid); +Exfat_api.c:int FsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid) +Exfat_api.c: struct super_block *sb = inode->i_sb; +Exfat_api.c: err = ffsCreateFile(inode, path, mode, fid); +Exfat_api.c:int FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount) +Exfat_api.c: struct super_block *sb = inode->i_sb; +Exfat_api.c: err = ffsReadFile(inode, fid, buffer, count, rcount); +Exfat_api.c:int FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount) +Exfat_api.c: struct super_block *sb = inode->i_sb; +Exfat_api.c: err = ffsWriteFile(inode, fid, buffer, count, wcount); +Exfat_api.c:int FsTruncateFile(struct inode *inode, u64 old_size, u64 new_size) +Exfat_api.c: struct super_block *sb = inode->i_sb; +Exfat_api.c: DPRINTK("FsTruncateFile entered (inode %p size %llu)\n", inode, new_size); +Exfat_api.c: err = ffsTruncateFile(inode, old_size, new_size); +Exfat_api.c:int FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) +Exfat_api.c:int FsRemoveFile(struct inode *inode, FILE_ID_T *fid) +Exfat_api.c: struct super_block *sb = inode->i_sb; +Exfat_api.c: err = ffsRemoveFile(inode, fid); +Exfat_api.c:int FsSetAttr(struct inode *inode, u32 attr) +Exfat_api.c: struct super_block *sb = inode->i_sb; +Exfat_api.c: err = ffsSetAttr(inode, attr); +Exfat_api.c:int FsReadStat(struct inode *inode, DIR_ENTRY_T *info) +Exfat_api.c: struct super_block *sb = inode->i_sb; +Exfat_api.c: err = ffsGetStat(inode, info); +Exfat_api.c:int FsWriteStat(struct inode *inode, DIR_ENTRY_T *info) +Exfat_api.c: struct super_block *sb = inode->i_sb; +Exfat_api.c: DPRINTK("FsWriteStat entered (inode %p info %p\n", inode, info); +Exfat_api.c: err = ffsSetStat(inode, info); +Exfat_api.c:int FsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu) +Exfat_api.c: struct super_block *sb = inode->i_sb; +Exfat_api.c: err = ffsMapCluster(inode, clu_offset, clu); +Exfat_api.c:int FsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid) +Exfat_api.c: struct super_block *sb = inode->i_sb; +Exfat_api.c: err = ffsCreateDir(inode, path, fid); +Exfat_api.c:int FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) +Exfat_api.c: struct super_block *sb = inode->i_sb; +Exfat_api.c: err = ffsReadDir(inode, dir_entry); +Exfat_api.c:int FsRemoveDir(struct inode *inode, FILE_ID_T *fid) +Exfat_api.c: struct super_block *sb = inode->i_sb; +Exfat_api.c: err = ffsRemoveDir(inode, fid); +Exfat_api.h: int FsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid); +Exfat_api.h: int FsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid); +Exfat_api.h: int FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount); +Exfat_api.h: int FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount); +Exfat_api.h: int FsTruncateFile(struct inode *inode, u64 old_size, u64 new_size); +Exfat_api.h: int FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); +Exfat_api.h: int FsRemoveFile(struct inode *inode, FILE_ID_T *fid); +Exfat_api.h: int FsSetAttr(struct inode *inode, u32 attr); +Exfat_api.h: int FsReadStat(struct inode *inode, DIR_ENTRY_T *info); +Exfat_api.h: int FsWriteStat(struct inode *inode, DIR_ENTRY_T *info); +Exfat_api.h: int FsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu); +Exfat_api.h: int FsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid); +Exfat_api.h: int FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry); +Exfat_api.h: int FsRemoveDir(struct inode *inode, FILE_ID_T *fid); +Exfat_core.c:s32 ffsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: ret = resolve_path(inode, path, &dir, &uni_name); +Exfat_core.c:s32 ffsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: ret = resolve_path(inode, path, &dir, &uni_name); +Exfat_core.c: ret = create_file(inode, &dir, &uni_name, mode, fid); +Exfat_core.c:s32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c:s32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c:s32 ffsTruncateFile(struct inode *inode, u64 old_size, u64 new_size) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: FILE_ID_T *fid = &(EXFAT_I(inode)->fid); +Exfat_core.c:static void update_parent_info(FILE_ID_T *fid, struct inode *parent_inode) +Exfat_core.c:s32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) +Exfat_core.c: struct inode *new_inode = new_dentry->d_inode; +Exfat_core.c:s32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: remove_file(inode, &dir, dentry); +Exfat_core.c:s32 ffsSetAttr(struct inode *inode, u32 attr) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: FILE_ID_T *fid = &(EXFAT_I(inode)->fid); +Exfat_core.c:s32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: FILE_ID_T *fid = &(EXFAT_I(inode)->fid); +Exfat_core.c:s32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: FILE_ID_T *fid = &(EXFAT_I(inode)->fid); +Exfat_core.c:s32 ffsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: FILE_ID_T *fid = &(EXFAT_I(inode)->fid); +Exfat_core.c: if (EXFAT_I(inode)->mmu_private == 0) +Exfat_core.c: num_clusters = (s32)((EXFAT_I(inode)->mmu_private-1) >> p_fs->cluster_size_bits) + 1; +Exfat_core.c: /* add number of new blocks to inode */ +Exfat_core.c: inode->i_blocks += num_alloced << (p_fs->cluster_size_bits - 9); +Exfat_core.c:s32 ffsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: ret = resolve_path(inode, path, &dir, &uni_name); +Exfat_core.c: ret = create_dir(inode, &dir, &uni_name, fid); +Exfat_core.c:s32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: FILE_ID_T *fid = &(EXFAT_I(inode)->fid); +Exfat_core.c:s32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: remove_file(inode, &dir, dentry); +Exfat_core.c:s32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, s32 num_entries) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: FILE_ID_T *fid = &(EXFAT_I(inode)->fid); +Exfat_core.c: size = i_size_read(inode); +Exfat_core.c: i_size_write(inode, i_size_read(inode)+p_fs->cluster_size); +Exfat_core.c: EXFAT_I(inode)->mmu_private += p_fs->cluster_size; +Exfat_core.c: EXFAT_I(inode)->fid.size += p_fs->cluster_size; +Exfat_core.c: EXFAT_I(inode)->fid.flags = p_dir->flags; +Exfat_core.c: inode->i_blocks += 1 << (p_fs->cluster_size_bits - 9); +Exfat_core.c:s32 resolve_path(struct inode *inode, char *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: FILE_ID_T *fid = &(EXFAT_I(inode)->fid); +Exfat_core.c: fid->size = i_size_read(inode); +Exfat_core.c:s32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: dentry = find_empty_entry(inode, p_dir, num_entries); +Exfat_core.c:s32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, u8 mode, FILE_ID_T *fid) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: dentry = find_empty_entry(inode, p_dir, num_entries); +Exfat_core.c:void remove_file(struct inode *inode, CHAIN_T *p_dir, s32 entry) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c:s32 rename_file(struct inode *inode, CHAIN_T *p_dir, s32 oldentry, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: newentry = find_empty_entry(inode, p_dir, num_new_entries); +Exfat_core.c: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) +Exfat_core.c: struct super_block *sb = inode->i_sb; +Exfat_core.c: newentry = find_empty_entry(inode, p_newdir, num_new_entries); +Exfat_core.h:s32 ffsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid); +Exfat_core.h:s32 ffsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid); +Exfat_core.h:s32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount); +Exfat_core.h:s32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount); +Exfat_core.h:s32 ffsTruncateFile(struct inode *inode, u64 old_size, u64 new_size); +Exfat_core.h:s32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); +Exfat_core.h:s32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid); +Exfat_core.h:s32 ffsSetAttr(struct inode *inode, u32 attr); +Exfat_core.h:s32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info); +Exfat_core.h:s32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info); +Exfat_core.h:s32 ffsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu); +Exfat_core.h:s32 ffsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid); +Exfat_core.h:s32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_ent); +Exfat_core.h:s32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid); +Exfat_core.h:s32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, s32 num_entries); +Exfat_core.h:s32 resolve_path(struct inode *inode, char *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname); +Exfat_core.h:s32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid); +Exfat_core.h:s32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, u8 mode, FILE_ID_T *fid); +Exfat_core.h:void remove_file(struct inode *inode, CHAIN_T *p_dir, s32 entry); +Exfat_core.h:s32 rename_file(struct inode *inode, CHAIN_T *p_dir, s32 old_entry, UNI_NAME_T *p_uniname, FILE_ID_T *fid); +Exfat_core.h: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); +Exfat_super.c:/* 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". */ +Exfat_super.c: * linux/fs/fat/inode.c +Exfat_super.c:static void _exfat_truncate(struct inode *inode, loff_t old_size); +Exfat_super.c:static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos); +Exfat_super.c:static int exfat_generic_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +Exfat_super.c:static int exfat_sync_inode(struct inode *inode); +Exfat_super.c:static struct inode *exfat_build_inode(struct super_block *sb, FILE_ID_T *fid, loff_t i_pos); +Exfat_super.c:static void exfat_detach(struct inode *inode); +Exfat_super.c:static void exfat_attach(struct inode *inode, loff_t i_pos); +Exfat_super.c:static int exfat_write_inode(struct inode *inode, int wait); +Exfat_super.c:static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc); +Exfat_super.c:static int exfat_d_hash(const struct dentry *dentry, const struct inode *inode, +Exfat_super.c:static int exfat_d_hashi(const struct dentry *dentry, const struct inode *inode, +Exfat_super.c:static int exfat_cmpi(const struct dentry *parent, const struct inode *pinode, +Exfat_super.c: const struct dentry *dentry, const struct inode *inode, +Exfat_super.c:static int exfat_cmp(const struct dentry *parent, const struct inode *pinode, +Exfat_super.c: const struct dentry *dentry, const struct inode *inode, +Exfat_super.c: struct inode *inode = file_inode(filp); +Exfat_super.c: struct inode *inode = filp->f_path.dentry->d_inode; +Exfat_super.c: struct super_block *sb = inode->i_sb; +Exfat_super.c: if ((p_fs->vol_type == EXFAT) || (inode->i_ino == EXFAT_ROOT_INO)) { +Exfat_super.c: if (inode->i_ino == EXFAT_ROOT_INO) +Exfat_super.c: inum = inode->i_ino; +Exfat_super.c: EXFAT_I(inode)->fid.size = i_size_read(inode); +Exfat_super.c: EXFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS; +Exfat_super.c: err = FsReadDir(inode, &de); +Exfat_super.c: cpos = EXFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS; +Exfat_super.c: inum = inode->i_ino; +Exfat_super.c: loff_t i_pos = ((loff_t) EXFAT_I(inode)->fid.start_clu << 32) | +Exfat_super.c: ((EXFAT_I(inode)->fid.rwoffset-1) & 0xffffffff); +Exfat_super.c: struct inode *tmp = exfat_iget(sb, i_pos); +Exfat_super.c:static int exfat_ioctl_volume_id(struct inode *dir) +Exfat_super.c:static int exfat_generic_ioctl(struct inode *inode, struct file *filp, +Exfat_super.c: struct inode *inode = filp->f_path.dentry->d_inode; +Exfat_super.c: struct inode *inode = filp->f_dentry->d_inode; +Exfat_super.c: return exfat_ioctl_volume_id(inode); +Exfat_super.c: struct super_block *sb = inode->i_sb; +Exfat_super.c: struct super_block *sb = inode->i_sb; +Exfat_super.c: struct inode *inode = filp->f_mapping->host; +Exfat_super.c: struct super_block *sb = inode->i_sb; +Exfat_super.c:static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, +Exfat_super.c:static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, +Exfat_super.c:static int exfat_create(struct inode *dir, struct dentry *dentry, int mode, +Exfat_super.c: struct inode *inode; +Exfat_super.c: inode = exfat_build_inode(sb, &fid, i_pos); +Exfat_super.c: if (IS_ERR(inode)) { +Exfat_super.c: err = PTR_ERR(inode); +Exfat_super.c: INC_IVERSION(inode); +Exfat_super.c: inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); +Exfat_super.c: d_instantiate(dentry, inode); +Exfat_super.c:static int exfat_find(struct inode *dir, struct qstr *qname, +Exfat_super.c:static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, +Exfat_super.c:static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, +Exfat_super.c: struct inode *inode; +Exfat_super.c: inode = NULL; +Exfat_super.c: inode = exfat_build_inode(sb, &fid, i_pos); +Exfat_super.c: if (IS_ERR(inode)) { +Exfat_super.c: err = PTR_ERR(inode); +Exfat_super.c: i_mode = inode->i_mode; +Exfat_super.c: if (S_ISLNK(i_mode) && !EXFAT_I(inode)->target) { +Exfat_super.c: EXFAT_I(inode)->target = kmalloc(i_size_read(inode)+1, GFP_KERNEL); +Exfat_super.c: if (!EXFAT_I(inode)->target) { +Exfat_super.c: FsReadFile(dir, &fid, EXFAT_I(inode)->target, i_size_read(inode), &ret); +Exfat_super.c: *(EXFAT_I(inode)->target + i_size_read(inode)) = '\0'; +Exfat_super.c: alias = d_find_alias(inode); +Exfat_super.c: iput(inode); +Exfat_super.c: dentry = d_splice_alias(inode, dentry); +Exfat_super.c: dentry = d_splice_alias(inode, dentry); +Exfat_super.c:static int exfat_unlink(struct inode *dir, struct dentry *dentry) +Exfat_super.c: struct inode *inode = dentry->d_inode; +Exfat_super.c: EXFAT_I(inode)->fid.size = i_size_read(inode); +Exfat_super.c: err = FsRemoveFile(dir, &(EXFAT_I(inode)->fid)); +Exfat_super.c: clear_nlink(inode); +Exfat_super.c: inode->i_mtime = inode->i_atime = current_time(inode); +Exfat_super.c: exfat_detach(inode); +Exfat_super.c: remove_inode_hash(inode); +Exfat_super.c:static int exfat_symlink(struct inode *dir, struct dentry *dentry, const char *target) +Exfat_super.c: struct inode *inode; +Exfat_super.c: inode = exfat_build_inode(sb, &fid, i_pos); +Exfat_super.c: if (IS_ERR(inode)) { +Exfat_super.c: err = PTR_ERR(inode); +Exfat_super.c: INC_IVERSION(inode); +Exfat_super.c: inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); +Exfat_super.c: EXFAT_I(inode)->target = kmalloc(len+1, GFP_KERNEL); +Exfat_super.c: if (!EXFAT_I(inode)->target) { +Exfat_super.c: memcpy(EXFAT_I(inode)->target, target, len+1); +Exfat_super.c: d_instantiate(dentry, inode); +Exfat_super.c:static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +Exfat_super.c:static int exfat_mkdir(struct inode *dir, struct dentry *dentry, int mode) +Exfat_super.c: struct inode *inode; +Exfat_super.c: inode = exfat_build_inode(sb, &fid, i_pos); +Exfat_super.c: if (IS_ERR(inode)) { +Exfat_super.c: err = PTR_ERR(inode); +Exfat_super.c: INC_IVERSION(inode); +Exfat_super.c: inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); +Exfat_super.c: d_instantiate(dentry, inode); +Exfat_super.c:static int exfat_rmdir(struct inode *dir, struct dentry *dentry) +Exfat_super.c: struct inode *inode = dentry->d_inode; +Exfat_super.c: EXFAT_I(inode)->fid.size = i_size_read(inode); +Exfat_super.c: err = FsRemoveDir(dir, &(EXFAT_I(inode)->fid)); +Exfat_super.c: clear_nlink(inode); +Exfat_super.c: inode->i_mtime = inode->i_atime = current_time(inode); +Exfat_super.c: exfat_detach(inode); +Exfat_super.c: remove_inode_hash(inode); +Exfat_super.c:static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, +Exfat_super.c: struct inode *new_dir, struct dentry *new_dentry, +Exfat_super.c:static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, +Exfat_super.c: struct inode *new_dir, struct dentry *new_dentry) +Exfat_super.c: struct inode *old_inode, *new_inode; +Exfat_super.c:static int exfat_cont_expand(struct inode *inode, loff_t size) +Exfat_super.c: struct address_space *mapping = inode->i_mapping; +Exfat_super.c: loff_t start = i_size_read(inode), count = size - i_size_read(inode); +Exfat_super.c: err = generic_cont_expand_simple(inode, size); +Exfat_super.c: inode->i_ctime = inode->i_mtime = current_time(inode); +Exfat_super.c: mark_inode_dirty(inode); +Exfat_super.c: if (IS_SYNC(inode)) { +Exfat_super.c: err2 = write_inode_now(inode, 1); +Exfat_super.c:static int exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode) +Exfat_super.c: if (!uid_eq(current_fsuid(), inode->i_uid)) +Exfat_super.c: if (current_fsuid() != inode->i_uid) +Exfat_super.c: if (in_group_p(inode->i_gid)) +Exfat_super.c: struct inode *inode, umode_t *mode_ptr) +Exfat_super.c: i_mode = inode->i_mode; +Exfat_super.c: if (exfat_mode_can_hold_ro(inode)) { +Exfat_super.c: /* If exfat_mode_can_hold_ro(inode) is false, can't change w bits. */ +Exfat_super.c: struct inode *inode = dentry->d_inode; +Exfat_super.c: && (attr->ia_size > i_size_read(inode))) { +Exfat_super.c: error = exfat_cont_expand(inode, attr->ia_size); +Exfat_super.c: && exfat_allow_set_time(sbi, inode)) { +Exfat_super.c: error = inode_change_ok(inode, attr); +Exfat_super.c: if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0) +Exfat_super.c: EXFAT_I(inode)->fid.size = i_size_read(inode); +Exfat_super.c: error = inode_setattr(inode, attr); +Exfat_super.c: old_size = i_size_read(inode); +Exfat_super.c: down_write(&EXFAT_I(inode)->truncate_lock); +Exfat_super.c: truncate_setsize(inode, attr->ia_size); +Exfat_super.c: _exfat_truncate(inode, old_size); +Exfat_super.c: up_write(&EXFAT_I(inode)->truncate_lock); +Exfat_super.c: truncate_setsize(inode, attr->ia_size); +Exfat_super.c: _exfat_truncate(inode, old_size); +Exfat_super.c: setattr_copy(inode, attr); +Exfat_super.c: mark_inode_dirty(inode); +Exfat_super.c: struct inode *inode = path->dentry->d_inode; +Exfat_super.c: struct inode *inode = dentry->d_inode; +Exfat_super.c: generic_fillattr(inode, stat); +Exfat_super.c: stat->blksize = EXFAT_SB(inode->i_sb)->fs_info.cluster_size; +Exfat_super.c:static const char *exfat_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) +Exfat_super.c: struct exfat_inode_info *ei = EXFAT_I(inode); +Exfat_super.c:static int exfat_file_release(struct inode *inode, struct file *filp) +Exfat_super.c: struct super_block *sb = inode->i_sb; +Exfat_super.c: EXFAT_I(inode)->fid.size = i_size_read(inode); +Exfat_super.c:static void _exfat_truncate(struct inode *inode, loff_t old_size) +Exfat_super.c: struct super_block *sb = inode->i_sb; +Exfat_super.c: if (EXFAT_I(inode)->mmu_private > i_size_read(inode)) +Exfat_super.c: EXFAT_I(inode)->mmu_private = i_size_read(inode); +Exfat_super.c: if (EXFAT_I(inode)->fid.start_clu == 0) +Exfat_super.c: err = FsTruncateFile(inode, old_size, i_size_read(inode)); +Exfat_super.c: inode->i_ctime = inode->i_mtime = current_time(inode); +Exfat_super.c: if (IS_DIRSYNC(inode)) +Exfat_super.c: (void) exfat_sync_inode(inode); +Exfat_super.c: mark_inode_dirty(inode); +Exfat_super.c: inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) +Exfat_super.c:static void exfat_truncate(struct inode *inode) +Exfat_super.c: _exfat_truncate(inode, i_size_read(inode)); +Exfat_super.c:static int exfat_bmap(struct inode *inode, sector_t sector, sector_t *phys, +Exfat_super.c: struct super_block *sb = inode->i_sb; +Exfat_super.c: if (inode->i_ino == EXFAT_ROOT_INO) { +Exfat_super.c: last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits; +Exfat_super.c: EXFAT_I(inode)->fid.size = i_size_read(inode); +Exfat_super.c: err = FsMapCluster(inode, clu_offset, &cluster); +Exfat_super.c:static int exfat_get_block(struct inode *inode, sector_t iblock, +Exfat_super.c: struct super_block *sb = inode->i_sb; +Exfat_super.c: unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; +Exfat_super.c: err = exfat_bmap(inode, iblock, &phys, &mapped_blocks, &create); +Exfat_super.c: EXFAT_I(inode)->mmu_private += max_blocks << sb->s_blocksize_bits; +Exfat_super.c: struct inode *inode = mapping->host; +Exfat_super.c: if (to > i_size_read(inode)) { +Exfat_super.c: truncate_pagecache(inode, i_size_read(inode)); +Exfat_super.c: truncate_pagecache(inode, to, i_size_read(inode)); +Exfat_super.c: EXFAT_I(inode)->fid.size = i_size_read(inode); +Exfat_super.c: _exfat_truncate(inode, i_size_read(inode)); +Exfat_super.c: struct inode *inode = mapping->host; +Exfat_super.c: FILE_ID_T *fid = &(EXFAT_I(inode)->fid); +Exfat_super.c: inode->i_mtime = inode->i_ctime = current_time(inode); +Exfat_super.c: mark_inode_dirty(inode); +Exfat_super.c: struct inode *inode = iocb->ki_filp->f_mapping->host; +Exfat_super.c: if (EXFAT_I(inode)->mmu_private < +Exfat_super.c: if (EXFAT_I(inode)->mmu_private < (offset + iov_length(iov, nr_segs))) +Exfat_super.c: if (EXFAT_I(inode)->mmu_private < (offset + iov_iter_count(iter))) +Exfat_super.c: if (EXFAT_I(inode)->mmu_private < iov_iter_count(iter)) +Exfat_super.c: ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block); +Exfat_super.c: ret = blockdev_direct_IO(iocb, inode, iter, +Exfat_super.c: ret = blockdev_direct_IO(rw, iocb, inode, iter, +Exfat_super.c: ret = blockdev_direct_IO(rw, iocb, inode, iter, +Exfat_super.c: ret = blockdev_direct_IO(rw, iocb, inode, iov, +Exfat_super.c: ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, +Exfat_super.c:static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos) +Exfat_super.c: struct inode *inode = NULL; +Exfat_super.c: inode = igrab(&info->vfs_inode); +Exfat_super.c: if (inode) +Exfat_super.c: return inode; +Exfat_super.c:static void exfat_attach(struct inode *inode, loff_t i_pos) +Exfat_super.c: struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); +Exfat_super.c: EXFAT_I(inode)->i_pos = i_pos; +Exfat_super.c: hlist_add_head(&EXFAT_I(inode)->i_hash_fat, head); +Exfat_super.c:static void exfat_detach(struct inode *inode) +Exfat_super.c: struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); +Exfat_super.c: hlist_del_init(&EXFAT_I(inode)->i_hash_fat); +Exfat_super.c: EXFAT_I(inode)->i_pos = 0; +Exfat_super.c:/* doesn't deal with root inode */ +Exfat_super.c:static int exfat_fill_inode(struct inode *inode, FILE_ID_T *fid) +Exfat_super.c: struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); +Exfat_super.c: memcpy(&(EXFAT_I(inode)->fid), fid, sizeof(FILE_ID_T)); +Exfat_super.c: FsReadStat(inode, &info); +Exfat_super.c: EXFAT_I(inode)->i_pos = 0; +Exfat_super.c: EXFAT_I(inode)->target = NULL; +Exfat_super.c: inode->i_uid = sbi->options.fs_uid; +Exfat_super.c: inode->i_gid = sbi->options.fs_gid; +Exfat_super.c: INC_IVERSION(inode); +Exfat_super.c: inode->i_generation = get_seconds(); +Exfat_super.c: inode->i_generation &= ~1; +Exfat_super.c: inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); +Exfat_super.c: inode->i_op = &exfat_dir_inode_operations; +Exfat_super.c: inode->i_fop = &exfat_dir_operations; +Exfat_super.c: i_size_write(inode, info.Size); +Exfat_super.c: EXFAT_I(inode)->mmu_private = i_size_read(inode); +Exfat_super.c: set_nlink(inode, info.NumSubdirs); +Exfat_super.c: inode->i_nlink = info.NumSubdirs; +Exfat_super.c: inode->i_generation |= 1; +Exfat_super.c: inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); +Exfat_super.c: inode->i_op = &exfat_symlink_inode_operations; +Exfat_super.c: i_size_write(inode, info.Size); +Exfat_super.c: EXFAT_I(inode)->mmu_private = i_size_read(inode); +Exfat_super.c: inode->i_generation |= 1; +Exfat_super.c: inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); +Exfat_super.c: inode->i_op = &exfat_file_inode_operations; +Exfat_super.c: inode->i_fop = &exfat_file_operations; +Exfat_super.c: inode->i_mapping->a_ops = &exfat_aops; +Exfat_super.c: inode->i_mapping->nrpages = 0; +Exfat_super.c: i_size_write(inode, info.Size); +Exfat_super.c: EXFAT_I(inode)->mmu_private = i_size_read(inode); +Exfat_super.c: exfat_save_attr(inode, info.Attr); +Exfat_super.c: inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) +Exfat_super.c: exfat_time_fat2unix(sbi, &inode->i_mtime, &info.ModifyTimestamp); +Exfat_super.c: exfat_time_fat2unix(sbi, &inode->i_ctime, &info.CreateTimestamp); +Exfat_super.c: exfat_time_fat2unix(sbi, &inode->i_atime, &info.AccessTimestamp); +Exfat_super.c:static struct inode *exfat_build_inode(struct super_block *sb, +Exfat_super.c: struct inode *inode; +Exfat_super.c: inode = exfat_iget(sb, i_pos); +Exfat_super.c: if (inode) +Exfat_super.c: inode = new_inode(sb); +Exfat_super.c: if (!inode) { +Exfat_super.c: inode = ERR_PTR(-ENOMEM); +Exfat_super.c: inode->i_ino = iunique(sb, EXFAT_ROOT_INO); +Exfat_super.c: SET_IVERSION(inode, 1); +Exfat_super.c: err = exfat_fill_inode(inode, fid); +Exfat_super.c: iput(inode); +Exfat_super.c: inode = ERR_PTR(err); +Exfat_super.c: exfat_attach(inode, i_pos); +Exfat_super.c: insert_inode_hash(inode); +Exfat_super.c: return inode; +Exfat_super.c:static int exfat_sync_inode(struct inode *inode) +Exfat_super.c: return exfat_write_inode(inode, 0); +Exfat_super.c: return exfat_write_inode(inode, NULL); +Exfat_super.c:static struct inode *exfat_alloc_inode(struct super_block *sb) +Exfat_super.c:static void exfat_destroy_inode(struct inode *inode) +Exfat_super.c: if (EXFAT_I(inode)->target) +Exfat_super.c: kfree(EXFAT_I(inode)->target); +Exfat_super.c: EXFAT_I(inode)->target = NULL; +Exfat_super.c: kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode)); +Exfat_super.c:static int exfat_write_inode(struct inode *inode, int wait) +Exfat_super.c:static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) +Exfat_super.c: struct super_block *sb = inode->i_sb; +Exfat_super.c: if (inode->i_ino == EXFAT_ROOT_INO) +Exfat_super.c: info.Attr = exfat_make_attr(inode); +Exfat_super.c: info.Size = i_size_read(inode); +Exfat_super.c: exfat_time_unix2fat(sbi, &inode->i_mtime, &info.ModifyTimestamp); +Exfat_super.c: exfat_time_unix2fat(sbi, &inode->i_ctime, &info.CreateTimestamp); +Exfat_super.c: exfat_time_unix2fat(sbi, &inode->i_atime, &info.AccessTimestamp); +Exfat_super.c: FsWriteStat(inode, &info); +Exfat_super.c:static void exfat_delete_inode(struct inode *inode) +Exfat_super.c: truncate_inode_pages(&inode->i_data, 0); +Exfat_super.c: clear_inode(inode); +Exfat_super.c:static void exfat_clear_inode(struct inode *inode) +Exfat_super.c: exfat_detach(inode); +Exfat_super.c: remove_inode_hash(inode); +Exfat_super.c:static void exfat_evict_inode(struct inode *inode) +Exfat_super.c: truncate_inode_pages(&inode->i_data, 0); +Exfat_super.c: if (!inode->i_nlink) +Exfat_super.c: i_size_write(inode, 0); +Exfat_super.c: invalidate_inode_buffers(inode); +Exfat_super.c: end_writeback(inode); +Exfat_super.c: clear_inode(inode); +Exfat_super.c: exfat_detach(inode); +Exfat_super.c: remove_inode_hash(inode); +Exfat_super.c:static struct inode *exfat_nfs_get_inode(struct super_block *sb, +Exfat_super.c: struct inode *inode = NULL; +Exfat_super.c: return inode; +Exfat_super.c: inode = ilookup(sb, ino); +Exfat_super.c: if (inode && generation && (inode->i_generation != generation)) { +Exfat_super.c: iput(inode); +Exfat_super.c: inode = NULL; +Exfat_super.c: return inode; +Exfat_super.c:static int exfat_read_root(struct inode *inode) +Exfat_super.c: struct super_block *sb = inode->i_sb; +Exfat_super.c: EXFAT_I(inode)->fid.dir.dir = p_fs->root_dir; +Exfat_super.c: EXFAT_I(inode)->fid.dir.flags = 0x01; +Exfat_super.c: EXFAT_I(inode)->fid.entry = -1; +Exfat_super.c: EXFAT_I(inode)->fid.start_clu = p_fs->root_dir; +Exfat_super.c: EXFAT_I(inode)->fid.flags = 0x01; +Exfat_super.c: EXFAT_I(inode)->fid.type = TYPE_DIR; +Exfat_super.c: EXFAT_I(inode)->fid.rwoffset = 0; +Exfat_super.c: EXFAT_I(inode)->fid.hint_last_off = -1; +Exfat_super.c: EXFAT_I(inode)->target = NULL; +Exfat_super.c: FsReadStat(inode, &info); +Exfat_super.c: inode->i_uid = sbi->options.fs_uid; +Exfat_super.c: inode->i_gid = sbi->options.fs_gid; +Exfat_super.c: INC_IVERSION(inode); +Exfat_super.c: inode->i_generation = 0; +Exfat_super.c: inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, S_IRWXUGO); +Exfat_super.c: inode->i_op = &exfat_dir_inode_operations; +Exfat_super.c: inode->i_fop = &exfat_dir_operations; +Exfat_super.c: i_size_write(inode, info.Size); +Exfat_super.c: inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) +Exfat_super.c: EXFAT_I(inode)->i_pos = ((loff_t) p_fs->root_dir << 32) | 0xffffffff; +Exfat_super.c: EXFAT_I(inode)->mmu_private = i_size_read(inode); +Exfat_super.c: exfat_save_attr(inode, ATTR_SUBDIR); +Exfat_super.c: inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); +Exfat_super.c: set_nlink(inode, info.NumSubdirs + 2); +Exfat_super.c: inode->i_nlink = info.NumSubdirs + 2; +Exfat_super.c: struct inode *root_inode = NULL; +Exfat_super.c: /* set up enough so that it can read an inode */ +Exfat_super.c: printk(KERN_ERR "[EXFAT] Getting the root inode failed\n"); +Exfat_super.h: struct inode *fat_inode; +Exfat_super.h: * EXFAT file system inode data in memory +Exfat_super.h: struct inode vfs_inode; +Exfat_super.h:static inline struct exfat_inode_info *EXFAT_I(struct inode *inode) +Exfat_super.h: return container_of(inode, struct exfat_inode_info, vfs_inode); +Exfat_super.h:static inline int exfat_mode_can_hold_ro(struct inode *inode) +Exfat_super.h: struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); +Exfat_super.h: if (S_ISDIR(inode->i_mode)) +Exfat_super.h:/* Return the FAT attribute byte for this inode */ +Exfat_super.h:static inline u32 exfat_make_attr(struct inode *inode) +Exfat_super.h: if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO)) +Exfat_super.h: return (EXFAT_I(inode)->fid.attr) | ATTR_READONLY; +Exfat_super.h: return EXFAT_I(inode)->fid.attr; +Exfat_super.h:static inline void exfat_save_attr(struct inode *inode, u32 attr) +Exfat_super.h: if (exfat_mode_can_hold_ro(inode)) +Exfat_super.h: EXFAT_I(inode)->fid.attr = attr & ATTR_RWMASK; +Exfat_super.h: EXFAT_I(inode)->fid.attr = attr & (ATTR_RWMASK | ATTR_READONLY); diff --git a/code/driver/source/fs/exfat/Untitled Project.WK3 b/code/driver/source/fs/exfat/Untitled Project.WK3 new file mode 100755 index 0000000000000000000000000000000000000000..eb45c3beb98bb52cf564d5c68530c569365112f4 GIT binary patch literal 90102 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 +#include +#include + +#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/code/driver/source/fs/exfat/exfat_api.h b/code/driver/source/fs/exfat/exfat_api.h new file mode 100755 index 000000000..84bdf612a --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_api.h @@ -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 +#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/code/driver/source/fs/exfat/exfat_bitmap.c b/code/driver/source/fs/exfat/exfat_bitmap.c new file mode 100755 index 000000000..b0672dd07 --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_bitmap.c @@ -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/code/driver/source/fs/exfat/exfat_bitmap.h b/code/driver/source/fs/exfat/exfat_bitmap.h new file mode 100755 index 000000000..4f482c7b2 --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_bitmap.h @@ -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 + +/*======================================================================*/ +/* */ +/* 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/code/driver/source/fs/exfat/exfat_blkdev.c b/code/driver/source/fs/exfat/exfat_blkdev.c new file mode 100755 index 000000000..eaccfd84e --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_blkdev.c @@ -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 +#include +#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/code/driver/source/fs/exfat/exfat_blkdev.h b/code/driver/source/fs/exfat/exfat_blkdev.h new file mode 100755 index 000000000..3363b591c --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_blkdev.h @@ -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 +#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/code/driver/source/fs/exfat/exfat_cache.c b/code/driver/source/fs/exfat/exfat_cache.c new file mode 100755 index 000000000..4130102e3 --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_cache.c @@ -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/code/driver/source/fs/exfat/exfat_cache.h b/code/driver/source/fs/exfat/exfat_cache.h new file mode 100755 index 000000000..540e31681 --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_cache.h @@ -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 +#include +#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/code/driver/source/fs/exfat/exfat_config.h b/code/driver/source/fs/exfat/exfat_config.h new file mode 100755 index 000000000..33c6525e4 --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_config.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* 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/code/driver/source/fs/exfat/exfat_core.c b/code/driver/source/fs/exfat/exfat_core.c new file mode 100755 index 000000000..143b72155 --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_core.c @@ -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 +#include +#include + +#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 +#include + +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, §or); + if (!ep) + goto err_out; + ep2 = ep; + } + + p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY); + p_fs->fs_func->set_entry_attr(ep, fid->attr); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + + if (modified) { + if (p_fs->fs_func->get_entry_flag(ep2) != fid->flags) + p_fs->fs_func->set_entry_flag(ep2, fid->flags); + + if (p_fs->fs_func->get_entry_size(ep2) != fid->size) + p_fs->fs_func->set_entry_size(ep2, fid->size); + + if (p_fs->fs_func->get_entry_clu0(ep2) != fid->start_clu) + p_fs->fs_func->set_entry_clu0(ep2, fid->start_clu); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + } + + if (p_fs->vol_type == EXFAT) { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + +#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, §or); + if (!ep) + return FFS_MEDIAERR; + ep2 = ep; + } + + p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY); + p_fs->fs_func->set_entry_attr(ep, fid->attr); + + p_fs->fs_func->set_entry_size(ep2, new_size); + if (new_size == 0) { + p_fs->fs_func->set_entry_flag(ep2, 0x01); + p_fs->fs_func->set_entry_clu0(ep2, CLUSTER_32(0)); + } + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + else { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + + /* (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<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, §or); + if (!ep) + return FFS_MEDIAERR; + } + + type = p_fs->fs_func->get_entry_type(ep); + + if (((type == TYPE_FILE) && (attr & ATTR_SUBDIR)) || + ((type == TYPE_DIR) && (!(attr & ATTR_SUBDIR)))) { + 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, §or); + 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, §or); + 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, §or); + if (!ep) + return FFS_MEDIAERR; + } + + if (p_fs->fs_func->get_entry_flag(ep) != fid->flags) + p_fs->fs_func->set_entry_flag(ep, fid->flags); + + if (p_fs->fs_func->get_entry_clu0(ep) != fid->start_clu) + p_fs->fs_func->set_entry_clu0(ep, fid->start_clu); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + } + + if (p_fs->vol_type == EXFAT) { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + + /* 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, §or); + if (!ep) + return FFS_MEDIAERR; + + type = p_fs->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) + break; + + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + buf_lock(sb, sector); + dir_entry->Attr = p_fs->fs_func->get_entry_attr(ep); + + p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE); + dir_entry->CreateTimestamp.Year = tm.year; + dir_entry->CreateTimestamp.Month = tm.mon; + dir_entry->CreateTimestamp.Day = tm.day; + dir_entry->CreateTimestamp.Hour = tm.hour; + dir_entry->CreateTimestamp.Minute = tm.min; + dir_entry->CreateTimestamp.Second = tm.sec; + dir_entry->CreateTimestamp.MilliSecond = 0; + + p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY); + dir_entry->ModifyTimestamp.Year = tm.year; + dir_entry->ModifyTimestamp.Month = tm.mon; + dir_entry->ModifyTimestamp.Day = tm.day; + dir_entry->ModifyTimestamp.Hour = tm.hour; + dir_entry->ModifyTimestamp.Minute = tm.min; + dir_entry->ModifyTimestamp.Second = tm.sec; + dir_entry->ModifyTimestamp.MilliSecond = 0; + + memset((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, §or); + 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, §or); + if (!file_ep) + return FFS_MEDIAERR; + + strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, §or); + if (!strm_ep) + return FFS_MEDIAERR; + + init_file_entry(file_ep, type); + buf_modify(sb, sector); + + init_strm_entry(strm_ep, flags, start_clu, size); + buf_modify(sb, sector); + + return FFS_SUCCESS; +} /* 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, §or); + if (!dos_ep) + return FFS_MEDIAERR; + + dos_ep->lcase = p_dosname->name_case; + memcpy(dos_ep->name, p_dosname->name, DOS_NAME_LENGTH); + buf_modify(sb, sector); + + if ((--num_entries) > 0) { + chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); + + for (i = 1; i < num_entries; i++) { + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); + if (!ext_ep) + return FFS_MEDIAERR; + + init_ext_entry(ext_ep, i, chksum, uniname); + buf_modify(sb, sector); + uniname += 13; + } + + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); + if (!ext_ep) + return FFS_MEDIAERR; + + init_ext_entry(ext_ep, i+0x40, chksum, uniname); + buf_modify(sb, sector); + } + + return FFS_SUCCESS; +} /* 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, §or); + 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, §or); + if (!strm_ep) + return FFS_MEDIAERR; + + strm_ep->name_len = p_uniname->name_len; + SET16_A(strm_ep->name_hash, p_uniname->name_hash); + buf_modify(sb, sector); + + for (i = 2; i < num_entries; i++) { + name_ep = (NAME_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+i, §or); + if (!name_ep) + return FFS_MEDIAERR; + + init_name_entry(name_ep, uniname); + buf_modify(sb, sector); + uniname += 15; + } + + update_dir_checksum(sb, p_dir, entry); + + return FFS_SUCCESS; +} /* 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, §or); + 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, §or); + 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, §or); + 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, §or); + if (!ep) + return -1; + p_fs->fs_func->set_entry_size(ep, size); + p_fs->fs_func->set_entry_flag(ep, p_dir->flags); + buf_modify(sb, sector); + + update_dir_checksum(sb, &(fid->dir), fid->entry); + } + } + + i_size_write(inode, i_size_read(inode)+p_fs->cluster_size); + EXFAT_I(inode)->mmu_private += p_fs->cluster_size; + EXFAT_I(inode)->fid.size += p_fs->cluster_size; + EXFAT_I(inode)->fid.flags = p_dir->flags; + inode->i_blocks += 1 << (p_fs->cluster_size_bits - 9); + } + + return dentry; +} /* 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, §or); + 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, §or_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, §or_new); + if (!epnew) { + buf_unlock(sb, sector_old); + return FFS_MEDIAERR; + } + + memcpy((void *) epnew, (void *) epold, DENTRY_SIZE); + if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) { + p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + buf_modify(sb, sector_new); + buf_unlock(sb, sector_old); + + if (p_fs->vol_type == EXFAT) { + epold = get_entry_in_dir(sb, p_dir, oldentry+1, §or_old); + buf_lock(sb, sector_old); + epnew = get_entry_in_dir(sb, p_dir, newentry+1, §or_new); + + if (!epold || !epnew) { + buf_unlock(sb, sector_old); + return FFS_MEDIAERR; + } + + memcpy((void *) epnew, (void *) epold, DENTRY_SIZE); + buf_modify(sb, sector_new); + buf_unlock(sb, sector_old); + } + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, newentry, num_new_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, 0, num_old_entries); + fid->entry = newentry; + } else { + if (p_fs->fs_func->get_entry_type(epold) == TYPE_FILE) { + p_fs->fs_func->set_entry_attr(epold, p_fs->fs_func->get_entry_attr(epold) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + buf_modify(sb, sector_old); + buf_unlock(sb, sector_old); + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, oldentry, num_new_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, num_new_entries, num_old_entries); + } + + return FFS_SUCCESS; +} /* 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, §or_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, §or_new); + if (!epnew) { + buf_unlock(sb, sector_mov); + return FFS_MEDIAERR; + } + + memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE); + if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) { + p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + buf_modify(sb, sector_new); + buf_unlock(sb, sector_mov); + + if (p_fs->vol_type == EXFAT) { + epmov = get_entry_in_dir(sb, p_olddir, oldentry+1, §or_mov); + buf_lock(sb, sector_mov); + epnew = get_entry_in_dir(sb, p_newdir, newentry+1, §or_new); + if (!epmov || !epnew) { + buf_unlock(sb, sector_mov); + return FFS_MEDIAERR; + } + + memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE); + buf_modify(sb, sector_new); + buf_unlock(sb, sector_mov); + } else if (p_fs->fs_func->get_entry_type(epnew) == TYPE_DIR) { + /* 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, §or_new); + if (!epnew) + return FFS_MEDIAERR; + + if (p_newdir->dir == p_fs->root_dir) + p_fs->fs_func->set_entry_clu0(epnew, CLUSTER_32(0)); + else + p_fs->fs_func->set_entry_clu0(epnew, p_newdir->dir); + buf_modify(sb, sector_new); + } + + ret = p_fs->fs_func->init_ext_entry(sb, p_newdir, newentry, num_new_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + p_fs->fs_func->delete_dir_entry(sb, p_olddir, oldentry, 0, num_old_entries); + + fid->dir.dir = p_newdir->dir; + fid->dir.size = p_newdir->size; + fid->dir.flags = p_newdir->flags; + + fid->entry = newentry; + + return FFS_SUCCESS; +} /* 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/code/driver/source/fs/exfat/exfat_core.h b/code/driver/source/fs/exfat/exfat_core.h new file mode 100755 index 000000000..52d05c700 --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_core.h @@ -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; +} +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/code/driver/source/fs/exfat/exfat_data.c b/code/driver/source/fs/exfat/exfat_data.c new file mode 100755 index 000000000..65da07aff --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_data.c @@ -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/code/driver/source/fs/exfat/exfat_data.h b/code/driver/source/fs/exfat/exfat_data.h new file mode 100755 index 000000000..53b0e3939 --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_data.h @@ -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/code/driver/source/fs/exfat/exfat_nls.c b/code/driver/source/fs/exfat/exfat_nls.c new file mode 100755 index 000000000..a48b3d05a --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_nls.c @@ -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 + +/*----------------------------------------------------------------------*/ +/* 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/code/driver/source/fs/exfat/exfat_nls.h b/code/driver/source/fs/exfat/exfat_nls.h new file mode 100755 index 000000000..bc516d762 --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_nls.h @@ -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 +#include + +#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/code/driver/source/fs/exfat/exfat_oal.c b/code/driver/source/fs/exfat/exfat_oal.c new file mode 100755 index 000000000..743544244 --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_oal.c @@ -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 +#include + +#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/code/driver/source/fs/exfat/exfat_oal.h b/code/driver/source/fs/exfat/exfat_oal.h new file mode 100755 index 000000000..b6dd7897a --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_oal.h @@ -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 +#include "exfat_config.h" +#include + +/*----------------------------------------------------------------------*/ +/* 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/code/driver/source/fs/exfat/exfat_super.c b/code/driver/source/fs/exfat/exfat_super.c new file mode 100755 index 000000000..148d29d66 --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_super.c @@ -0,0 +1,2753 @@ +/* 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 +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) +#include +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +#define USE_NEW_IVERSION_API +#define INC_IVERSION(x) (inode_inc_iversion(x)) +#define GET_IVERSION(x) (inode_peek_iversion_raw(x)) +#define SET_IVERSION(x,y) (inode_set_iversion(x, y)) +#else +#define INC_IVERSION(x) (x->i_version++) +#define GET_IVERSION(x) (x->i_version) +#define SET_IVERSION(x,y) (x->i_version = y) +#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 timespec64 *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++; + +#if 0 + 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; +#else + 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; + + if (!sbi->options.tz_set) + ts->tv_sec += sys_tz.tz_minuteswest * SECS_PER_MIN; + else + ts->tv_sec -= sbi->options.time_offset * SECS_PER_MIN; +#endif + ts->tv_nsec = 0; +} + +/* Convert linear UNIX date to a FAT time/date pair. */ +void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec64 *ts, + DATE_TIME_T *tp) +{ + time_t second = ts->tv_sec; + time_t day, month, year; + time_t ld; + +#if 0 + second -= sys_tz.tz_minuteswest * SECS_PER_MIN; +#else + if (sbi->options.tz_set) + second += sbi->options.time_offset * SECS_PER_MIN; + else + second -= sys_tz.tz_minuteswest * SECS_PER_MIN; +#endif + + /* 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; + } + INC_IVERSION(dir); + 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; + } + INC_IVERSION(inode); + 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 = GET_IVERSION(dentry->d_parent->d_inode); + 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 = GET_IVERSION(dentry->d_parent->d_inode); +#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 = GET_IVERSION(dentry->d_parent->d_inode); + } +#else + dentry = d_splice_alias(inode, dentry); + if (dentry) + dentry->d_time = GET_IVERSION(dentry->d_parent->d_inode); +#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; + } + INC_IVERSION(dir); + 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; + } + + INC_IVERSION(dir); + 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; + } + INC_IVERSION(inode); + 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 = GET_IVERSION(dentry->d_parent->d_inode); + 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; + } + INC_IVERSION(dir); + 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; + } + INC_IVERSION(inode); + 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 = GET_IVERSION(dentry->d_parent->d_inode); + 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; + } + INC_IVERSION(dir); + 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; + } + INC_IVERSION(new_dir); + 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); + } + INC_IVERSION(old_dir); + 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; + INC_IVERSION(inode); + 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); + SET_IVERSION(inode, 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->tz_set) { + if (opts->time_offset) + seq_printf(m, ",time_offset=%d", opts->time_offset); + else + seq_puts(m, ",tz=UTC"); + } + 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_tz_utc, + Opt_time_offset, + 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_tz_utc, "tz=UTC"}, + {Opt_time_offset, "time_offset=%d"}, + {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->tz_set = 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_time_offset: + if (match_int(&args[0], &option)) + return -EINVAL; + if (option < -12 * 60 || option > 12 * 60) + return -EINVAL; + opts->tz_set = 1; + opts->time_offset = option; + break; + case Opt_tz_utc: + opts->tz_set = 1; + opts->time_offset = 0; + 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; + INC_IVERSION(inode); + 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; + SET_IVERSION(root_inode, 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/code/driver/source/fs/exfat/exfat_super.h b/code/driver/source/fs/exfat/exfat_super.h new file mode 100755 index 000000000..46a26ba04 --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_super.h @@ -0,0 +1,173 @@ +/* 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 +#include +#include +#include +#include +#include + +#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 */ + int time_offset; /* Offset of timestamps from UTC (in minutes) */ + 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 */ + unsigned tz_set:1; /* Filesystem timestamps' offset set */ +}; + +#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/code/driver/source/fs/exfat/exfat_upcase.c b/code/driver/source/fs/exfat/exfat_upcase.c new file mode 100755 index 000000000..3807f37ca --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_upcase.c @@ -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/code/driver/source/fs/exfat/exfat_version.h b/code/driver/source/fs/exfat/exfat_version.h new file mode 100755 index 000000000..a93fa46be --- /dev/null +++ b/code/driver/source/fs/exfat/exfat_version.h @@ -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" diff --git a/code/lib/external/Makefile b/code/lib/external/Makefile index 0ced0a06a..3e9cdcd2a 100755 --- a/code/lib/external/Makefile +++ b/code/lib/external/Makefile @@ -6,7 +6,7 @@ INSTALL_DIR =${EXTLIB}/__install #MAKEFILE_LIST = freetype libnl openssl # openssl for tee MULTI_CORES ?= $(shell grep -c ^processor /proc/cpuinfo) -MAKEFILE_LIST = openssl libnl +MAKEFILE_LIST = openssl libnl exfat-utils XML2 := libxml2-2.9.3 @@ -19,6 +19,7 @@ FFMPEG := ffmpeg-3.0 LIBNL := libnl-3.2.27 OPENSSL := openssl-1.0.2d CURL := curl-7.45.0 +EXFAT_UTILS := exfat-utils-1.2.7 unexport CC unexport CPP @@ -164,6 +165,17 @@ endif @rm -rf ${INSTALL_DIR}/lib/pkgconfig @echo $(NVT_CURL_SSL) > ${CURL}/.nvt_finish +exfat-utils: init + @echo ">>>>>>>>>>>>>>>>>>> $@ compiling >>>>>>>>>>>>>>>>>>>" + @if [ ! -f ${EXFAT_UTILS}/.nvt_finish ]; then \ + $(call check_exist, ${EXFAT_UTILS}); \ + cd ${EXFAT_UTILS}; ./configure --host=${NVT_HOST} --prefix=${EXTERNAL}/${INSTALL_DIR}; make; \ + fi + @mkdir -p ${EXTERNAL}/${INSTALL_DIR}/sbin/; + @cd ${EXFAT_UTILS}; cp -f ./mkfs/mkexfatfs ${ROOTFS_DIR}/rootfs/sbin/; + @${CROSS_COMPILE}strip ${ROOTFS_DIR}/rootfs/sbin/mkexfatfs; + @touch ${EXFAT_UTILS}/.nvt_finish + install: @echo ">>>>>>>>>>>>>>>>>>> $@ >>>>>>>>>>>>>>>>>>>" @if [ -x ${INSTALL_DIR}/include ]; then \ @@ -181,4 +193,4 @@ install: clean: @echo ">>>>>>>>>>>>>>>>>>> Remove >>>>>>>>>>>>>>>>>>>" @rm -rf ${INSTALL_DIR} - @rm -rf ${XML2} ${AXTLS} ${FUSE} ${FREETYPE} ${FFMPEG} ${LIBNL} ${SQLITE3} ${OPENSSL} ${CURL} ${UNWIND} ${LIBP11} + @rm -rf ${XML2} ${AXTLS} ${FUSE} ${FREETYPE} ${FFMPEG} ${LIBNL} ${SQLITE3} ${OPENSSL} ${CURL} ${UNWIND} ${LIBP11} ${EXFAT_UTILS} diff --git a/configs/Linux/cfg_565_HUNTING_EVB_LINUX_4G_S550/make_post.sh b/configs/Linux/cfg_565_HUNTING_EVB_LINUX_4G_S550/make_post.sh index 5ef61d24f..6d69af810 100755 --- a/configs/Linux/cfg_565_HUNTING_EVB_LINUX_4G_S550/make_post.sh +++ b/configs/Linux/cfg_565_HUNTING_EVB_LINUX_4G_S550/make_post.sh @@ -102,6 +102,7 @@ MV_KO_LIST=(\ /lib/modules/$KERVER/kernel/drivers/usb/serial/option.ko \ /lib/modules/$KERVER/kernel/drivers/usb/class/cdc-wdm.ko \ /lib/modules/$KERVER/kernel/drivers/net/usb/qmi_wwan.ko \ +/lib/modules/$KERVER/extra/fs/exfat/exfat.ko \ ) diff --git a/rtos/code/application/source/cardv/SrcCode/System/SysStrg_Exe.c b/rtos/code/application/source/cardv/SrcCode/System/SysStrg_Exe.c index e96ff6893..fdb3192a5 100644 --- a/rtos/code/application/source/cardv/SrcCode/System/SysStrg_Exe.c +++ b/rtos/code/application/source/cardv/SrcCode/System/SysStrg_Exe.c @@ -343,8 +343,8 @@ void System_OnStrgInit_FS(void) GxStrg_SetConfigEx(0, FILE_CFG_MAX_OPEN_FILE, 10); #endif // support exFAT -// GxStrg_SetConfigEx(0, FILE_CFG_SUPPORT_EXFAT, TRUE); - GxStrg_SetConfigEx(0, FILE_CFG_SUPPORT_EXFAT, FALSE); + GxStrg_SetConfigEx(0, FILE_CFG_SUPPORT_EXFAT, TRUE); + // GxStrg_SetConfigEx(0, FILE_CFG_SUPPORT_EXFAT, FALSE); // mount path strncpy(mount_path, "/mnt/sd", sizeof(mount_path) - 1); mount_path[sizeof(mount_path) - 1] = '\0';