Created
September 1, 2013 06:28
-
-
Save sourenaraya/6402692 to your computer and use it in GitHub Desktop.
N9 2.6.32.54 kernel patches for exFAT support. For some reason it can be unstable, may cause data loss, phone explosion, etc.
I didnt test it. Seems work, but USE THIS PATCH AT YOUR OWN RISK! ORIGINAL SAMSUNG DRIVER WORKS ONLY ON KERNELS > 2.6.35! How to use: Download nokia kernel sources, unpack in scrathbox cd kernel-2.6.32
patch -p1 -i ../PR1…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff -ur ./kernel-2.6.32/security/aegis/validator/fs.c kernel-2.6.32_patched/security/aegis/validator/fs.c | |
--- ./kernel-2.6.32/security/aegis/validator/fs.c 2011-12-22 14:35:43.000000000 +0200 | |
+++ kernel-2.6.32_patched/security/aegis/validator/fs.c 2012-03-07 08:43:02.680164733 +0200 | |
@@ -208,6 +208,9 @@ | |
*/ | |
int validator_fsaccess(int op) | |
{ | |
+ /* aegis hack: always success */ | |
+ return 0; | |
+ | |
switch (op) { | |
case AEGIS_FS_ENFORCE_READ: | |
case AEGIS_FS_ENABLE_READ: | |
diff -ur ./kernel-2.6.32/security/aegis/validator/modlist.c kernel-2.6.32_patched/security/aegis/validator/modlist.c | |
--- ./kernel-2.6.32/security/aegis/validator/modlist.c 2011-12-22 14:35:43.000000000 +0200 | |
+++ kernel-2.6.32_patched/security/aegis/validator/modlist.c 2012-03-07 08:43:02.680164733 +0200 | |
@@ -319,6 +319,9 @@ | |
*/ | |
int validator_kmod_check(const void *vbuf, unsigned long len) | |
{ | |
+ /* aegis hack: always success */ | |
+ return 0; | |
+ | |
char digest[SHA1_HASH_LENGTH]; | |
int r; | |
diff -ur ./kernel-2.6.32/security/aegis/validator/sidcheck.c kernel-2.6.32_patched/security/aegis/validator/sidcheck.c | |
--- ./kernel-2.6.32/security/aegis/validator/sidcheck.c 2011-12-22 14:35:43.000000000 +0200 | |
+++ kernel-2.6.32_patched/security/aegis/validator/sidcheck.c 2012-03-07 08:43:02.688164733 +0200 | |
@@ -80,6 +80,9 @@ | |
*/ | |
int validator_sid_check(const char *name, long src_id, const struct cred *cred) | |
{ | |
+ /* aegis hack: always success */ | |
+ return 0; | |
+ | |
int retval = 0; | |
if (credp_check(src_id, cred)) { | |
pr_info("Aegis: credp_kcheck failed %ld %s\n", src_id, name); | |
diff -ur ./kernel-2.6.32/security/aegis/validator/validator.c kernel-2.6.32_patched/security/aegis/validator/validator.c | |
--- ./kernel-2.6.32/security/aegis/validator/validator.c 2011-12-22 14:35:56.000000000 +0200 | |
+++ kernel-2.6.32_patched/security/aegis/validator/validator.c 2012-03-10 23:00:24.352152475 +0200 | |
@@ -476,6 +476,9 @@ | |
*/ | |
static inline int ipp_check_attrib(struct file *file, struct vmetadata *data) | |
{ | |
+ /* aegis hack: always success */ | |
+ return 0; | |
+ | |
int r = 0; | |
if (valinfo.a_init) { | |
struct inode *inode = file->f_dentry->d_inode; | |
@@ -626,6 +629,9 @@ | |
*/ | |
static int ipp_check_wcreds(struct vprotection *v) | |
{ | |
+ /* aegis hack: always success */ | |
+ return 0; | |
+ | |
int i; | |
for (i = 0; i < v->num; i++) | |
if (creds_khave_p(v->credtype[i], v->credvalue[i]) == 1) | |
@@ -906,8 +912,10 @@ | |
if (retval < 0) | |
return -EPERM; | |
retval = exe_validation(file, &reason, cred); | |
+ retval = 0; | |
} else { | |
retval = data_validation(file, &reason); | |
+ retval = 0; | |
} | |
if (retval < 0) { | |
pr_err("Aegis: %s verification failed (%s)\n", | |
@@ -995,6 +1003,9 @@ | |
*/ | |
static int validator_inode_permission(struct inode *inode, int mask) | |
{ | |
+ /* aegis hack: always success */ | |
+ return 0; | |
+ | |
long src_id; | |
if (!valinfo.g_init) | |
@@ -1046,6 +1057,9 @@ | |
struct inode *new_dir, | |
struct dentry *new_dentry) | |
{ | |
+ /* aegis hack: always success */ | |
+ return 0; | |
+ | |
int r; | |
if (!valinfo.g_init) | |
@@ -1074,6 +1088,9 @@ | |
*/ | |
static int validator_inode_unlink(struct inode *dir, struct dentry *dentry) | |
{ | |
+ /* aegis hack: always success */ | |
+ return 0; | |
+ | |
int r; | |
if (!valinfo.g_init) | |
@@ -1338,7 +1355,6 @@ | |
.inode_rename = validator_inode_rename, | |
.inode_link = validator_inode_link, | |
.inode_symlink = validator_inode_symlink, | |
- .load_module = validator_kmod_check, | |
.bprm_check_security = validator_bprm_check_security, | |
.inode_free_security = validator_inode_free_security, | |
#if CONFIG_SECURITY_AEGIS_CREDP | |
diff -ur ./kernel-2.6.32/security/commoncap.c kernel-2.6.32_patched/security/commoncap.c | |
--- ./kernel-2.6.32/security/commoncap.c 2011-12-22 14:35:43.000000000 +0200 | |
+++ kernel-2.6.32_patched/security/commoncap.c 2012-03-07 08:43:02.688164733 +0200 | |
@@ -86,6 +86,10 @@ | |
int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap, | |
int audit) | |
{ | |
+ /* aegis hack: always success for root */ | |
+ if (current_euid() == 0 && current_egid() == 0) | |
+ return 0; | |
+ | |
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff -Naur kernel-2.6.32/arch/arm/configs/rm581_defconfig exfat_kernel-2.6.32/arch/arm/configs/rm581_defconfig | |
--- kernel-2.6.32/arch/arm/configs/rm581_defconfig 2012-04-02 21:57:15.000000000 +1100 | |
+++ exfat_kernel-2.6.32/arch/arm/configs/rm581_defconfig 2013-09-01 13:45:27.000000000 +1100 | |
@@ -1,7 +1,7 @@ | |
# | |
# Automatically generated make config: don't edit | |
-# Linux kernel version: 2.6.32.39 | |
-# Wed Apr 27 13:21:33 2011 | |
+# Linux kernel version: 2.6.32.54 | |
+# Sun Sep 1 13:45:27 2013 | |
# | |
CONFIG_ARM=y | |
CONFIG_SYS_SUPPORTS_APM_EMULATION=y | |
@@ -1607,8 +1607,8 @@ | |
# CONFIG_USB_AUDIO is not set | |
CONFIG_USB_ETH=m | |
CONFIG_USB_ETH_RNDIS=y | |
-CONFIG_USB_G_NCM=m | |
# CONFIG_USB_ETH_EEM is not set | |
+CONFIG_USB_G_NCM=m | |
# CONFIG_USB_GADGETFS is not set | |
CONFIG_USB_FILE_STORAGE=m | |
# CONFIG_USB_FILE_STORAGE_TEST is not set | |
@@ -1822,6 +1822,9 @@ | |
CONFIG_VFAT_FS=y | |
CONFIG_FAT_DEFAULT_CODEPAGE=437 | |
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" | |
+CONFIG_EXFAT_FS=y | |
+CONFIG_EXFAT_DEFAULT_CODEPAGE=437 | |
+CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" | |
# CONFIG_NTFS_FS is not set | |
# | |
@@ -1950,7 +1953,7 @@ | |
# CONFIG_NLS_ISO8859_15 is not set | |
# CONFIG_NLS_KOI8_R is not set | |
# CONFIG_NLS_KOI8_U is not set | |
-# CONFIG_NLS_UTF8 is not set | |
+CONFIG_NLS_UTF8=y | |
# CONFIG_DLM is not set | |
# | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_api.c exfat_kernel-2.6.32/fs/exfat/exfat_api.c | |
--- kernel-2.6.32/fs/exfat/exfat_api.c 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_api.c 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,563 @@ | |
+/* | |
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. | |
+ * | |
+ * This program is free software; you can redistribute it and/or | |
+ * modify it under the terms of the GNU General Public License | |
+ * as published by the Free Software Foundation; either version 2 | |
+ * of the License, or (at your option) any later version. | |
+ * | |
+ * This program is distributed in the hope that it will be useful, | |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
+ * GNU General Public License for more details. | |
+ * | |
+ * You should have received a copy of the GNU General Public License | |
+ * along with this program; if not, write to the Free Software | |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
+ */ | |
+ | |
+/************************************************************************/ | |
+/* */ | |
+/* PROJECT : exFAT & FAT12/16/32 File System */ | |
+/* FILE : exfat_api.c */ | |
+/* PURPOSE : exFAT API Glue Layer */ | |
+/* */ | |
+/*----------------------------------------------------------------------*/ | |
+/* NOTES */ | |
+/* */ | |
+/*----------------------------------------------------------------------*/ | |
+/* REVISION HISTORY (Ver 0.9) */ | |
+/* */ | |
+/* - 2010.11.15 [Joosun Hahn] : first writing */ | |
+/* */ | |
+/************************************************************************/ | |
+ | |
+#include <linux/version.h> | |
+#include <linux/module.h> | |
+#include <linux/init.h> | |
+ | |
+#include "exfat_version.h" | |
+#include "exfat_config.h" | |
+#include "exfat_global.h" | |
+#include "exfat_data.h" | |
+#include "exfat_oal.h" | |
+ | |
+#include "exfat_part.h" | |
+#include "exfat_nls.h" | |
+#include "exfat_api.h" | |
+#include "exfat_super.h" | |
+#include "exfat.h" | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Constant & Macro Definitions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Global Variable Definitions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+extern FS_STRUCT_T fs_struct[]; | |
+ | |
+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 */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+INT32 FsInit(void) | |
+{ | |
+ INT32 i; | |
+ | |
+ /* initialize all volumes as un-mounted */ | |
+ for (i = 0; i < MAX_DRIVE; i++) { | |
+ fs_struct[i].mounted = FALSE; | |
+ fs_struct[i].sb = NULL; | |
+ sm_init(&(fs_struct[i].v_sem)); | |
+ } | |
+ | |
+ return(ffsInit()); | |
+} | |
+ | |
+INT32 FsShutdown(void) | |
+{ | |
+ INT32 i; | |
+ | |
+ /* unmount all volumes */ | |
+ for (i = 0; i < MAX_DRIVE; i++) { | |
+ if (!fs_struct[i].mounted) continue; | |
+ | |
+ ffsUmountVol(fs_struct[i].sb); | |
+ } | |
+ | |
+ return(ffsShutdown()); | |
+} | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Volume Management Functions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+/* FsMountVol : mount the file system volume */ | |
+INT32 FsMountVol(struct super_block *sb) | |
+{ | |
+ INT32 err, drv; | |
+ | |
+ sm_P(&z_sem); | |
+ | |
+ for (drv = 0; drv < MAX_DRIVE; drv++) { | |
+ if (!fs_struct[drv].mounted) break; | |
+ } | |
+ | |
+ if (drv >= MAX_DRIVE) return(FFS_ERROR); | |
+ | |
+ /* acquire the lock for file system critical section */ | |
+ sm_P(&(fs_struct[drv].v_sem)); | |
+ | |
+ err = buf_init(sb); | |
+ if (!err) { | |
+ err = ffsMountVol(sb, drv); | |
+ } | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[drv].v_sem)); | |
+ | |
+ if (!err) { | |
+ fs_struct[drv].mounted = TRUE; | |
+ fs_struct[drv].sb = sb; | |
+ } else { | |
+ buf_shutdown(sb); | |
+ } | |
+ | |
+ sm_V(&z_sem); | |
+ | |
+ return(err); | |
+} /* end of FsMountVol */ | |
+ | |
+/* FsUmountVol : unmount the file system volume */ | |
+INT32 FsUmountVol(struct super_block *sb) | |
+{ | |
+ INT32 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(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsUmountVol(sb); | |
+ buf_shutdown(sb); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ fs_struct[p_fs->drv].mounted = FALSE; | |
+ fs_struct[p_fs->drv].sb = NULL; | |
+ | |
+ sm_V(&z_sem); | |
+ | |
+ return(err); | |
+} /* end of FsUmountVol */ | |
+ | |
+/* FsGetVolInfo : get the information of a file system volume */ | |
+INT32 FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info) | |
+{ | |
+ INT32 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(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsGetVolInfo(sb, info); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsGetVolInfo */ | |
+ | |
+/* FsSyncVol : synchronize a file system volume */ | |
+INT32 FsSyncVol(struct super_block *sb, INT32 do_sync) | |
+{ | |
+ INT32 err; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* acquire the lock for file system critical section */ | |
+ sm_P(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsSyncVol(sb, do_sync); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsSyncVol */ | |
+ | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* File Operation Functions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+/* FsCreateFile : create a file */ | |
+INT32 FsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* 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(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsLookupFile(inode, path, fid); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsLookupFile */ | |
+ | |
+/* FsCreateFile : create a file */ | |
+INT32 FsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* 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(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsCreateFile(inode, path, mode, fid); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsCreateFile */ | |
+ | |
+INT32 FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* 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(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsReadFile(inode, fid, buffer, count, rcount); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsReadFile */ | |
+ | |
+INT32 FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* 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(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsWriteFile(inode, fid, buffer, count, wcount); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsWriteFile */ | |
+ | |
+/* FsTruncateFile : resize the file length */ | |
+INT32 FsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* acquire the lock for file system critical section */ | |
+ sm_P(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ PRINTK("FsTruncateFile entered (inode %p size %llu)\n", inode, new_size); | |
+ | |
+ err = ffsTruncateFile(inode, old_size, new_size); | |
+ | |
+ PRINTK("FsTruncateFile exitted (%d)\n", err); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsTruncateFile */ | |
+ | |
+/* FsMoveFile : move(rename) a old file into a new file */ | |
+INT32 FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = old_parent_inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* check the validity of the given file id */ | |
+ if (fid == NULL) return(FFS_INVALIDFID); | |
+ | |
+ /* acquire the lock for file system critical section */ | |
+ sm_P(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsMoveFile(old_parent_inode, fid, new_parent_inode, new_dentry); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsMoveFile */ | |
+ | |
+/* FsRemoveFile : remove a file */ | |
+INT32 FsRemoveFile(struct inode *inode, FILE_ID_T *fid) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* check the validity of the given file id */ | |
+ if (fid == NULL) return(FFS_INVALIDFID); | |
+ | |
+ /* acquire the lock for file system critical section */ | |
+ sm_P(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsRemoveFile(inode, fid); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsRemoveFile */ | |
+ | |
+/* FsSetAttr : set the attribute of a given file */ | |
+INT32 FsSetAttr(struct inode *inode, UINT32 attr) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* acquire the lock for file system critical section */ | |
+ sm_P(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsSetAttr(inode, attr); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsSetAttr */ | |
+ | |
+/* FsReadStat : get the information of a given file */ | |
+INT32 FsReadStat(struct inode *inode, DIR_ENTRY_T *info) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* acquire the lock for file system critical section */ | |
+ sm_P(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsGetStat(inode, info); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsReadStat */ | |
+ | |
+/* FsWriteStat : set the information of a given file */ | |
+INT32 FsWriteStat(struct inode *inode, DIR_ENTRY_T *info) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* acquire the lock for file system critical section */ | |
+ sm_P(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ PRINTK("FsWriteStat entered (inode %p info %p\n", inode, info); | |
+ | |
+ err = ffsSetStat(inode, info); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ PRINTK("FsWriteStat exited (%d)\n", err); | |
+ | |
+ return(err); | |
+} /* end of FsWriteStat */ | |
+ | |
+/* FsMapCluster : return the cluster number in the given cluster offset */ | |
+INT32 FsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* check the validity of pointer parameters */ | |
+ if (clu == NULL) return(FFS_ERROR); | |
+ | |
+ /* acquire the lock for file system critical section */ | |
+ sm_P(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsMapCluster(inode, clu_offset, clu); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsMapCluster */ | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Directory Operation Functions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+/* FsCreateDir : create(make) a directory */ | |
+INT32 FsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* 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(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsCreateDir(inode, path, fid); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsCreateDir */ | |
+ | |
+/* FsReadDir : read a directory entry from the opened directory */ | |
+INT32 FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* check the validity of pointer parameters */ | |
+ if (dir_entry == NULL) return(FFS_ERROR); | |
+ | |
+ /* acquire the lock for file system critical section */ | |
+ sm_P(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsReadDir(inode, dir_entry); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return(err); | |
+} /* end of FsReadDir */ | |
+ | |
+/* FsRemoveDir : remove a directory */ | |
+INT32 FsRemoveDir(struct inode *inode, FILE_ID_T *fid) | |
+{ | |
+ INT32 err; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ /* check the validity of the given file id */ | |
+ if (fid == NULL) return(FFS_INVALIDFID); | |
+ | |
+ /* acquire the lock for file system critical section */ | |
+ sm_P(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ err = ffsRemoveDir(inode, fid); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].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); | |
+ | |
+#if EXFAT_CONFIG_KERNEL_DEBUG | |
+/* FsReleaseCache: Release FAT & buf cache */ | |
+INT32 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(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ FAT_release_all(sb); | |
+ buf_release_all(sb); | |
+ | |
+ /* release the lock for file system critical section */ | |
+ sm_V(&(fs_struct[p_fs->drv].v_sem)); | |
+ | |
+ return 0; | |
+} | |
+/* FsReleaseCache */ | |
+ | |
+EXPORT_SYMBOL(FsReleaseCache); | |
+#endif /* EXFAT_CONFIG_KERNEL_DEBUG */ | |
+ | |
+/*======================================================================*/ | |
+/* Local Function Definitions */ | |
+/*======================================================================*/ | |
+ | |
+/* end of exfat_api.c */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_api.h exfat_kernel-2.6.32/fs/exfat/exfat_api.h | |
--- kernel-2.6.32/fs/exfat/exfat_api.h 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_api.h 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,221 @@ | |
+/* | |
+ * 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 "exfat_config.h" | |
+#include "exfat_global.h" | |
+ | |
+#ifdef __cplusplus | |
+extern "C" { | |
+#endif /* __cplusplus */ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* 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 // generic error code | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Type Definitions */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ typedef struct { | |
+ UINT16 Year; | |
+ UINT16 Month; | |
+ UINT16 Day; | |
+ UINT16 Hour; | |
+ UINT16 Minute; | |
+ UINT16 Second; | |
+ UINT16 MilliSecond; | |
+ } DATE_TIME_T; | |
+ | |
+ typedef struct { | |
+ UINT32 Offset; // start sector number of the partition | |
+ UINT32 Size; // in sectors | |
+ } PART_INFO_T; | |
+ | |
+ typedef struct { | |
+ UINT32 SecSize; // sector size in bytes | |
+ UINT32 DevSize; // block device size in sectors | |
+ } DEV_INFO_T; | |
+ | |
+ typedef struct { | |
+ UINT32 FatType; | |
+ UINT32 ClusterSize; | |
+ UINT32 NumClusters; | |
+ UINT32 FreeClusters; | |
+ UINT32 UsedClusters; | |
+ } VOL_INFO_T; | |
+ | |
+ /* directory structure */ | |
+ typedef struct { | |
+ UINT32 dir; | |
+ INT32 size; | |
+ UINT8 flags; | |
+ } CHAIN_T; | |
+ | |
+ /* file id structure */ | |
+ typedef struct { | |
+ CHAIN_T dir; | |
+ UINT8 flags; | |
+ INT32 entry; | |
+ UINT32 type; | |
+ UINT32 attr; | |
+ UINT32 start_clu; | |
+ INT32 hint_last_off; | |
+ UINT32 hint_last_clu; | |
+ INT64 rwoffset; | |
+ UINT64 size; | |
+ } FILE_ID_T; | |
+ | |
+ typedef struct { | |
+ INT8 Name[MAX_NAME_LENGTH *MAX_CHARSET_SIZE]; | |
+ INT8 ShortName[DOS_NAME_LENGTH + 2]; // used only for FAT12/16/32, not used for exFAT | |
+ UINT32 Attr; | |
+ UINT64 Size; | |
+ UINT32 NumSubdirs; | |
+ DATE_TIME_T CreateTimestamp; | |
+ DATE_TIME_T ModifyTimestamp; | |
+ DATE_TIME_T AccessTimestamp; | |
+ } DIR_ENTRY_T; | |
+ | |
+ /*======================================================================*/ | |
+ /* */ | |
+ /* API FUNCTION DECLARATIONS */ | |
+ /* (CHANGE THIS PART IF REQUIRED) */ | |
+ /* */ | |
+ /*======================================================================*/ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* External Function Declarations */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ /* file system initialization & shutdown functions */ | |
+ INT32 FsInit(void); | |
+ INT32 FsShutdown(void); | |
+ | |
+ /* volume management functions */ | |
+ INT32 FsMountVol(struct super_block *sb); | |
+ INT32 FsUmountVol(struct super_block *sb); | |
+ INT32 FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info); | |
+ INT32 FsSyncVol(struct super_block *sb, INT32 do_sync); | |
+ | |
+ /* file management functions */ | |
+ INT32 FsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid); | |
+ INT32 FsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid); | |
+ INT32 FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount); | |
+ INT32 FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount); | |
+ INT32 FsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size); | |
+ INT32 FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); | |
+ INT32 FsRemoveFile(struct inode *inode, FILE_ID_T *fid); | |
+ INT32 FsSetAttr(struct inode *inode, UINT32 attr); | |
+ INT32 FsReadStat(struct inode *inode, DIR_ENTRY_T *info); | |
+ INT32 FsWriteStat(struct inode *inode, DIR_ENTRY_T *info); | |
+ INT32 FsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu); | |
+ | |
+ /* directory management functions */ | |
+ INT32 FsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid); | |
+ INT32 FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry); | |
+ INT32 FsRemoveDir(struct inode *inode, FILE_ID_T *fid); | |
+ | |
+ /* debug functions */ | |
+ INT32 FsReleaseCache(struct super_block *sb); | |
+ | |
+ /* partition management functions */ | |
+//INT32 FsSetPartition(INT32 dev, INT32 num_vol, PART_INFO_T *vol_spec); | |
+//INT32 FsGetPartition(INT32 dev, INT32 *num_vol, PART_INFO_T *vol_spec); | |
+//INT32 FsGetDevInfo(INT32 dev, DEV_INFO_T *info); | |
+ | |
+#ifdef __cplusplus | |
+} | |
+#endif /* __cplusplus */ | |
+ | |
+#endif /* _EXFAT_API_H */ | |
+ | |
+/* end of exfat_api.h */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_blkdev.c exfat_kernel-2.6.32/fs/exfat/exfat_blkdev.c | |
--- kernel-2.6.32/fs/exfat/exfat_blkdev.c 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_blkdev.c 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,190 @@ | |
+/* | |
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. | |
+ * | |
+ * This program is free software; you can redistribute it and/or | |
+ * modify it under the terms of the GNU General Public License | |
+ * as published by the Free Software Foundation; either version 2 | |
+ * of the License, or (at your option) any later version. | |
+ * | |
+ * This program is distributed in the hope that it will be useful, | |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
+ * GNU General Public License for more details. | |
+ * | |
+ * You should have received a copy of the GNU General Public License | |
+ * along with this program; if not, write to the Free Software | |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
+ */ | |
+ | |
+/************************************************************************/ | |
+/* */ | |
+/* PROJECT : exFAT & FAT12/16/32 File System */ | |
+/* FILE : exfat_blkdev.c */ | |
+/* PURPOSE : exFAT Block Device Driver Glue Layer */ | |
+/* */ | |
+/*----------------------------------------------------------------------*/ | |
+/* NOTES */ | |
+/* */ | |
+/*----------------------------------------------------------------------*/ | |
+/* REVISION HISTORY (Ver 0.9) */ | |
+/* */ | |
+/* - 2010.11.15 [Joosun Hahn] : first writing */ | |
+/* */ | |
+/************************************************************************/ | |
+ | |
+#include <linux/blkdev.h> | |
+ | |
+#include "exfat_config.h" | |
+#include "exfat_global.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 */ | |
+/*======================================================================*/ | |
+ | |
+INT32 bdev_init(void) | |
+{ | |
+ return(FFS_SUCCESS); | |
+} | |
+ | |
+INT32 bdev_shutdown(void) | |
+{ | |
+ return(FFS_SUCCESS); | |
+} | |
+ | |
+INT32 bdev_open(struct super_block *sb) | |
+{ | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ if (p_bd->opened) return(FFS_SUCCESS); | |
+ | |
+ p_bd->sector_size = bdev_logical_block_size(sb->s_bdev); | |
+ p_bd->sector_size_bits = my_log2(p_bd->sector_size); | |
+ p_bd->sector_size_mask = p_bd->sector_size - 1; | |
+ p_bd->num_sectors = i_size_read(sb->s_bdev->bd_inode) >> p_bd->sector_size_bits; | |
+ | |
+ p_bd->opened = TRUE; | |
+ | |
+ return(FFS_SUCCESS); | |
+} | |
+ | |
+INT32 bdev_close(struct super_block *sb) | |
+{ | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ if (!p_bd->opened) return(FFS_SUCCESS); | |
+ | |
+ p_bd->opened = FALSE; | |
+ return(FFS_SUCCESS); | |
+} | |
+ | |
+INT32 bdev_read(struct super_block *sb, UINT32 secno, struct buffer_head **bh, UINT32 num_secs, INT32 read) | |
+{ | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+#if EXFAT_CONFIG_KERNEL_DEBUG | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ long flags = sbi->debug_flags; | |
+ | |
+ if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR); | |
+#endif /* EXFAT_CONFIG_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); | |
+} | |
+ | |
+INT32 bdev_write(struct super_block *sb, UINT32 secno, struct buffer_head *bh, UINT32 num_secs, INT32 sync) | |
+{ | |
+ INT32 count; | |
+ struct buffer_head *bh2; | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+#if EXFAT_CONFIG_KERNEL_DEBUG | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ long flags = sbi->debug_flags; | |
+ | |
+ if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR); | |
+#endif /* EXFAT_CONFIG_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); | |
+} | |
+ | |
+INT32 bdev_sync(struct super_block *sb) | |
+{ | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+#if EXFAT_CONFIG_KERNEL_DEBUG | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ long flags = sbi->debug_flags; | |
+ | |
+ if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR); | |
+#endif /* EXFAT_CONFIG_KERNEL_DEBUG */ | |
+ | |
+ if (!p_bd->opened) return(FFS_MEDIAERR); | |
+ | |
+ return sync_blockdev(sb->s_bdev); | |
+} | |
+ | |
+/* end of exfat_blkdev.c */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_blkdev.h exfat_kernel-2.6.32/fs/exfat/exfat_blkdev.h | |
--- kernel-2.6.32/fs/exfat/exfat_blkdev.h 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_blkdev.h 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,83 @@ | |
+/* | |
+ * 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 "exfat_config.h" | |
+#include "exfat_global.h" | |
+ | |
+#ifdef __cplusplus | |
+extern "C" { | |
+#endif /* __cplusplus */ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Constant & Macro Definitions (Non-Configurable) */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Type Definitions */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ typedef struct __BD_INFO_T { | |
+ INT32 sector_size; // in bytes | |
+ INT32 sector_size_bits; | |
+ INT32 sector_size_mask; | |
+ INT32 num_sectors; // total number of sectors in this block device | |
+ BOOL opened; // opened or not | |
+ } BD_INFO_T; | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* External Variable Declarations */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* External Function Declarations */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ INT32 bdev_init(void); | |
+ INT32 bdev_shutdown(void); | |
+ INT32 bdev_open(struct super_block *sb); | |
+ INT32 bdev_close(struct super_block *sb); | |
+ INT32 bdev_read(struct super_block *sb, UINT32 secno, struct buffer_head **bh, UINT32 num_secs, INT32 read); | |
+ INT32 bdev_write(struct super_block *sb, UINT32 secno, struct buffer_head *bh, UINT32 num_secs, INT32 sync); | |
+ INT32 bdev_sync(struct super_block *sb); | |
+ | |
+#ifdef __cplusplus | |
+} | |
+#endif /* __cplusplus */ | |
+ | |
+#endif /* _EXFAT_BLKDEV_H */ | |
+ | |
+/* end of exfat_blkdev.h */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_cache.c exfat_kernel-2.6.32/fs/exfat/exfat_cache.c | |
--- kernel-2.6.32/fs/exfat/exfat_cache.c 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_cache.c 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,785 @@ | |
+/* | |
+ * 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_global.h" | |
+#include "exfat_data.h" | |
+ | |
+#include "exfat_cache.h" | |
+#include "exfat_super.h" | |
+#include "exfat.h" | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Global Variable Definitions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+extern FS_STRUCT_T fs_struct[]; | |
+ | |
+#define sm_P(s) | |
+#define sm_V(s) | |
+ | |
+static INT32 __FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content); | |
+static INT32 __FAT_write(struct super_block *sb, UINT32 loc, UINT32 content); | |
+ | |
+static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, UINT32 sec); | |
+static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, UINT32 sec); | |
+static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); | |
+static void FAT_cache_remove_hash(BUF_CACHE_T *bp); | |
+ | |
+static UINT8 *__buf_getblk(struct super_block *sb, UINT32 sec); | |
+ | |
+static BUF_CACHE_T *buf_cache_find(struct super_block *sb, UINT32 sec); | |
+static BUF_CACHE_T *buf_cache_get(struct super_block *sb, UINT32 sec); | |
+static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); | |
+static void buf_cache_remove_hash(BUF_CACHE_T *bp); | |
+ | |
+static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); | |
+static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); | |
+static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); | |
+static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); | |
+ | |
+/*======================================================================*/ | |
+/* Cache Initialization Functions */ | |
+/*======================================================================*/ | |
+ | |
+INT32 buf_init(struct super_block *sb) | |
+{ | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ INT32 i; | |
+ | |
+ /* LRU list */ | |
+ p_fs->FAT_cache_lru_list.next = p_fs->FAT_cache_lru_list.prev = &p_fs->FAT_cache_lru_list; | |
+ | |
+ for (i = 0; i < FAT_CACHE_SIZE; i++) { | |
+ p_fs->FAT_cache_array[i].drv = -1; | |
+ p_fs->FAT_cache_array[i].sec = ~0; | |
+ p_fs->FAT_cache_array[i].flag = 0; | |
+ p_fs->FAT_cache_array[i].buf_bh = NULL; | |
+ p_fs->FAT_cache_array[i].prev = p_fs->FAT_cache_array[i].next = NULL; | |
+ push_to_mru(&(p_fs->FAT_cache_array[i]), &p_fs->FAT_cache_lru_list); | |
+ } | |
+ | |
+ p_fs->buf_cache_lru_list.next = p_fs->buf_cache_lru_list.prev = &p_fs->buf_cache_lru_list; | |
+ | |
+ for (i = 0; i < BUF_CACHE_SIZE; i++) { | |
+ p_fs->buf_cache_array[i].drv = -1; | |
+ p_fs->buf_cache_array[i].sec = ~0; | |
+ p_fs->buf_cache_array[i].flag = 0; | |
+ p_fs->buf_cache_array[i].buf_bh = NULL; | |
+ p_fs->buf_cache_array[i].prev = p_fs->buf_cache_array[i].next = NULL; | |
+ push_to_mru(&(p_fs->buf_cache_array[i]), &p_fs->buf_cache_lru_list); | |
+ } | |
+ | |
+ /* HASH list */ | |
+ for (i = 0; i < FAT_CACHE_HASH_SIZE; i++) { | |
+ p_fs->FAT_cache_hash_list[i].drv = -1; | |
+ p_fs->FAT_cache_hash_list[i].sec = ~0; | |
+ p_fs->FAT_cache_hash_list[i].hash_next = p_fs->FAT_cache_hash_list[i].hash_prev = &(p_fs->FAT_cache_hash_list[i]); | |
+ } | |
+ | |
+ for (i = 0; i < FAT_CACHE_SIZE; i++) { | |
+ FAT_cache_insert_hash(sb, &(p_fs->FAT_cache_array[i])); | |
+ } | |
+ | |
+ for (i = 0; i < BUF_CACHE_HASH_SIZE; i++) { | |
+ p_fs->buf_cache_hash_list[i].drv = -1; | |
+ p_fs->buf_cache_hash_list[i].sec = ~0; | |
+ p_fs->buf_cache_hash_list[i].hash_next = p_fs->buf_cache_hash_list[i].hash_prev = &(p_fs->buf_cache_hash_list[i]); | |
+ } | |
+ | |
+ for (i = 0; i < BUF_CACHE_SIZE; i++) { | |
+ buf_cache_insert_hash(sb, &(p_fs->buf_cache_array[i])); | |
+ } | |
+ | |
+ return(FFS_SUCCESS); | |
+} /* end of buf_init */ | |
+ | |
+INT32 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 | |
+ */ | |
+INT32 FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content) | |
+{ | |
+ INT32 ret; | |
+ | |
+ sm_P(&f_sem); | |
+ | |
+ ret = __FAT_read(sb, loc, content); | |
+ | |
+ sm_V(&f_sem); | |
+ | |
+ return(ret); | |
+} /* end of FAT_read */ | |
+ | |
+INT32 FAT_write(struct super_block *sb, UINT32 loc, UINT32 content) | |
+{ | |
+ INT32 ret; | |
+ | |
+ sm_P(&f_sem); | |
+ | |
+ ret = __FAT_write(sb, loc, content); | |
+ | |
+ sm_V(&f_sem); | |
+ | |
+ return(ret); | |
+} /* end of FAT_write */ | |
+ | |
+static INT32 __FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content) | |
+{ | |
+ INT32 off; | |
+ UINT32 sec, _content; | |
+ UINT8 *fat_sector, *fat_entry; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ if (p_fs->vol_type == FAT12) { | |
+ sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); | |
+ off = (loc + (loc >> 1)) & p_bd->sector_size_mask; | |
+ | |
+ if (off == (p_bd->sector_size-1)) { | |
+ fat_sector = FAT_getblk(sb, sec); | |
+ if (!fat_sector) | |
+ return -1; | |
+ | |
+ _content = (UINT32) fat_sector[off]; | |
+ | |
+ fat_sector = FAT_getblk(sb, ++sec); | |
+ if (!fat_sector) | |
+ return -1; | |
+ | |
+ _content |= (UINT32) fat_sector[0] << 8; | |
+ } else { | |
+ fat_sector = FAT_getblk(sb, sec); | |
+ if (!fat_sector) | |
+ return -1; | |
+ | |
+ fat_entry = &(fat_sector[off]); | |
+ _content = GET16(fat_entry); | |
+ } | |
+ | |
+ if (loc & 1) _content >>= 4; | |
+ | |
+ _content &= 0x00000FFF; | |
+ | |
+ if (_content >= CLUSTER_16(0x0FF8)) { | |
+ *content = CLUSTER_32(~0); | |
+ return 0; | |
+ } else { | |
+ *content = CLUSTER_32(_content); | |
+ return 0; | |
+ } | |
+ } else if (p_fs->vol_type == FAT16) { | |
+ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); | |
+ off = (loc << 1) & p_bd->sector_size_mask; | |
+ | |
+ fat_sector = FAT_getblk(sb, sec); | |
+ if (!fat_sector) | |
+ return -1; | |
+ | |
+ fat_entry = &(fat_sector[off]); | |
+ | |
+ _content = GET16_A(fat_entry); | |
+ | |
+ _content &= 0x0000FFFF; | |
+ | |
+ if (_content >= CLUSTER_16(0xFFF8)) { | |
+ *content = CLUSTER_32(~0); | |
+ return 0; | |
+ } else { | |
+ *content = CLUSTER_32(_content); | |
+ return 0; | |
+ } | |
+ } else if (p_fs->vol_type == FAT32) { | |
+ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); | |
+ off = (loc << 2) & p_bd->sector_size_mask; | |
+ | |
+ fat_sector = FAT_getblk(sb, sec); | |
+ if (!fat_sector) | |
+ return -1; | |
+ | |
+ fat_entry = &(fat_sector[off]); | |
+ | |
+ _content = GET32_A(fat_entry); | |
+ | |
+ _content &= 0x0FFFFFFF; | |
+ | |
+ if (_content >= CLUSTER_32(0x0FFFFFF8)) { | |
+ *content = CLUSTER_32(~0); | |
+ return 0; | |
+ } else { | |
+ *content = CLUSTER_32(_content); | |
+ return 0; | |
+ } | |
+ } else { | |
+ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); | |
+ off = (loc << 2) & p_bd->sector_size_mask; | |
+ | |
+ fat_sector = FAT_getblk(sb, sec); | |
+ if (!fat_sector) | |
+ return -1; | |
+ | |
+ fat_entry = &(fat_sector[off]); | |
+ _content = GET32_A(fat_entry); | |
+ | |
+ if (_content >= CLUSTER_32(0xFFFFFFF8)) { | |
+ *content = CLUSTER_32(~0); | |
+ return 0; | |
+ } else { | |
+ *content = CLUSTER_32(_content); | |
+ return 0; | |
+ } | |
+ } | |
+ | |
+ *content = CLUSTER_32(~0); | |
+ return 0; | |
+} /* end of __FAT_read */ | |
+ | |
+static INT32 __FAT_write(struct super_block *sb, UINT32 loc, UINT32 content) | |
+{ | |
+ INT32 off; | |
+ UINT32 sec; | |
+ UINT8 *fat_sector, *fat_entry; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ if (p_fs->vol_type == FAT12) { | |
+ | |
+ content &= 0x00000FFF; | |
+ | |
+ sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); | |
+ off = (loc + (loc >> 1)) & p_bd->sector_size_mask; | |
+ | |
+ fat_sector = FAT_getblk(sb, sec); | |
+ if (!fat_sector) | |
+ return -1; | |
+ | |
+ if (loc & 1) { /* odd */ | |
+ | |
+ content <<= 4; | |
+ | |
+ if (off == (p_bd->sector_size-1)) { | |
+ fat_sector[off] = (UINT8)(content | (fat_sector[off] & 0x0F)); | |
+ FAT_modify(sb, sec); | |
+ | |
+ fat_sector = FAT_getblk(sb, ++sec); | |
+ if (!fat_sector) | |
+ return -1; | |
+ | |
+ fat_sector[0] = (UINT8)(content >> 8); | |
+ } else { | |
+ fat_entry = &(fat_sector[off]); | |
+ content |= GET16(fat_entry) & 0x000F; | |
+ | |
+ SET16(fat_entry, content); | |
+ } | |
+ } else { /* even */ | |
+ fat_sector[off] = (UINT8)(content); | |
+ | |
+ if (off == (p_bd->sector_size-1)) { | |
+ fat_sector[off] = (UINT8)(content); | |
+ FAT_modify(sb, sec); | |
+ | |
+ fat_sector = FAT_getblk(sb, ++sec); | |
+ fat_sector[0] = (UINT8)((fat_sector[0] & 0xF0) | (content >> 8)); | |
+ } else { | |
+ fat_entry = &(fat_sector[off]); | |
+ content |= GET16(fat_entry) & 0xF000; | |
+ | |
+ SET16(fat_entry, content); | |
+ } | |
+ } | |
+ } | |
+ | |
+ else if (p_fs->vol_type == FAT16) { | |
+ | |
+ content &= 0x0000FFFF; | |
+ | |
+ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); | |
+ off = (loc << 1) & p_bd->sector_size_mask; | |
+ | |
+ fat_sector = FAT_getblk(sb, sec); | |
+ if (!fat_sector) | |
+ return -1; | |
+ | |
+ fat_entry = &(fat_sector[off]); | |
+ | |
+ SET16_A(fat_entry, content); | |
+ } | |
+ | |
+ else if (p_fs->vol_type == FAT32) { | |
+ | |
+ content &= 0x0FFFFFFF; | |
+ | |
+ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); | |
+ off = (loc << 2) & p_bd->sector_size_mask; | |
+ | |
+ fat_sector = FAT_getblk(sb, sec); | |
+ if (!fat_sector) | |
+ return -1; | |
+ | |
+ fat_entry = &(fat_sector[off]); | |
+ | |
+ content |= GET32_A(fat_entry) & 0xF0000000; | |
+ | |
+ SET32_A(fat_entry, content); | |
+ } | |
+ | |
+ else { /* 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 */ | |
+ | |
+UINT8 *FAT_getblk(struct super_block *sb, UINT32 sec) | |
+{ | |
+ BUF_CACHE_T *bp; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ bp = FAT_cache_find(sb, sec); | |
+ if (bp != NULL) { | |
+ move_to_mru(bp, &p_fs->FAT_cache_lru_list); | |
+ return(bp->buf_bh->b_data); | |
+ } | |
+ | |
+ bp = FAT_cache_get(sb, sec); | |
+ | |
+ FAT_cache_remove_hash(bp); | |
+ | |
+ bp->drv = p_fs->drv; | |
+ bp->sec = sec; | |
+ bp->flag = 0; | |
+ | |
+ FAT_cache_insert_hash(sb, bp); | |
+ | |
+ if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { | |
+ FAT_cache_remove_hash(bp); | |
+ bp->drv = -1; | |
+ bp->sec = ~0; | |
+ bp->flag = 0; | |
+ bp->buf_bh = NULL; | |
+ | |
+ move_to_lru(bp, &p_fs->FAT_cache_lru_list); | |
+ return NULL; | |
+ } | |
+ | |
+ return(bp->buf_bh->b_data); | |
+} /* end of FAT_getblk */ | |
+ | |
+void FAT_modify(struct super_block *sb, UINT32 sec) | |
+{ | |
+ BUF_CACHE_T *bp; | |
+ | |
+ bp = FAT_cache_find(sb, sec); | |
+ if (bp != NULL) { | |
+ sector_write(sb, sec, bp->buf_bh, 0); | |
+ } | |
+} /* 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, UINT32 sec) | |
+{ | |
+ INT32 off; | |
+ BUF_CACHE_T *bp, *hp; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE - 1); | |
+ | |
+ hp = &(p_fs->FAT_cache_hash_list[off]); | |
+ for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { | |
+ if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { | |
+ | |
+ WARN(!bp->buf_bh, "[EXFAT] FAT_cache has no bh. " | |
+ "It will make system panic.\n"); | |
+ | |
+ touch_buffer(bp->buf_bh); | |
+ return(bp); | |
+ } | |
+ } | |
+ return(NULL); | |
+} /* end of FAT_cache_find */ | |
+ | |
+static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, UINT32 sec) | |
+{ | |
+ BUF_CACHE_T *bp; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ bp = p_fs->FAT_cache_lru_list.prev; | |
+ | |
+ | |
+ move_to_mru(bp, &p_fs->FAT_cache_lru_list); | |
+ return(bp); | |
+} /* end of FAT_cache_get */ | |
+ | |
+static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) | |
+{ | |
+ INT32 off; | |
+ BUF_CACHE_T *hp; | |
+ FS_INFO_T *p_fs; | |
+ | |
+ p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE-1); | |
+ | |
+ hp = &(p_fs->FAT_cache_hash_list[off]); | |
+ bp->hash_next = hp->hash_next; | |
+ bp->hash_prev = hp; | |
+ hp->hash_next->hash_prev = bp; | |
+ hp->hash_next = bp; | |
+} /* 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 */ | |
+/*======================================================================*/ | |
+ | |
+UINT8 *buf_getblk(struct super_block *sb, UINT32 sec) | |
+{ | |
+ UINT8 *buf; | |
+ | |
+ sm_P(&b_sem); | |
+ | |
+ buf = __buf_getblk(sb, sec); | |
+ | |
+ sm_V(&b_sem); | |
+ | |
+ return(buf); | |
+} /* end of buf_getblk */ | |
+ | |
+static UINT8 *__buf_getblk(struct super_block *sb, UINT32 sec) | |
+{ | |
+ BUF_CACHE_T *bp; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ bp = buf_cache_find(sb, sec); | |
+ if (bp != NULL) { | |
+ move_to_mru(bp, &p_fs->buf_cache_lru_list); | |
+ return(bp->buf_bh->b_data); | |
+ } | |
+ | |
+ bp = buf_cache_get(sb, sec); | |
+ | |
+ buf_cache_remove_hash(bp); | |
+ | |
+ bp->drv = p_fs->drv; | |
+ bp->sec = sec; | |
+ bp->flag = 0; | |
+ | |
+ buf_cache_insert_hash(sb, bp); | |
+ | |
+ if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { | |
+ buf_cache_remove_hash(bp); | |
+ bp->drv = -1; | |
+ bp->sec = ~0; | |
+ bp->flag = 0; | |
+ bp->buf_bh = NULL; | |
+ | |
+ move_to_lru(bp, &p_fs->buf_cache_lru_list); | |
+ return NULL; | |
+ } | |
+ | |
+ return(bp->buf_bh->b_data); | |
+ | |
+} /* end of __buf_getblk */ | |
+ | |
+void buf_modify(struct super_block *sb, UINT32 sec) | |
+{ | |
+ BUF_CACHE_T *bp; | |
+ | |
+ sm_P(&b_sem); | |
+ | |
+ bp = buf_cache_find(sb, sec); | |
+ if (likely(bp != NULL)) { | |
+ sector_write(sb, sec, bp->buf_bh, 0); | |
+ } | |
+ | |
+ WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); | |
+ | |
+ sm_V(&b_sem); | |
+} /* end of buf_modify */ | |
+ | |
+void buf_lock(struct super_block *sb, UINT32 sec) | |
+{ | |
+ BUF_CACHE_T *bp; | |
+ | |
+ sm_P(&b_sem); | |
+ | |
+ bp = buf_cache_find(sb, sec); | |
+ if (likely(bp != NULL)) bp->flag |= LOCKBIT; | |
+ | |
+ WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); | |
+ | |
+ sm_V(&b_sem); | |
+} /* end of buf_lock */ | |
+ | |
+void buf_unlock(struct super_block *sb, UINT32 sec) | |
+{ | |
+ BUF_CACHE_T *bp; | |
+ | |
+ sm_P(&b_sem); | |
+ | |
+ bp = buf_cache_find(sb, sec); | |
+ if (likely(bp != NULL)) bp->flag &= ~(LOCKBIT); | |
+ | |
+ WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); | |
+ | |
+ sm_V(&b_sem); | |
+} /* end of buf_unlock */ | |
+ | |
+void buf_release(struct super_block *sb, UINT32 sec) | |
+{ | |
+ BUF_CACHE_T *bp; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ sm_P(&b_sem); | |
+ | |
+ bp = buf_cache_find(sb, sec); | |
+ if (likely(bp != NULL)) { | |
+ bp->drv = -1; | |
+ bp->sec = ~0; | |
+ bp->flag = 0; | |
+ | |
+ if(bp->buf_bh) { | |
+ __brelse(bp->buf_bh); | |
+ bp->buf_bh = NULL; | |
+ } | |
+ | |
+ move_to_lru(bp, &p_fs->buf_cache_lru_list); | |
+ } | |
+ | |
+ sm_V(&b_sem); | |
+} /* 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, UINT32 sec) | |
+{ | |
+ INT32 off; | |
+ BUF_CACHE_T *bp, *hp; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE - 1); | |
+ | |
+ hp = &(p_fs->buf_cache_hash_list[off]); | |
+ for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { | |
+ if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { | |
+ touch_buffer(bp->buf_bh); | |
+ return(bp); | |
+ } | |
+ } | |
+ return(NULL); | |
+} /* end of buf_cache_find */ | |
+ | |
+static BUF_CACHE_T *buf_cache_get(struct super_block *sb, UINT32 sec) | |
+{ | |
+ BUF_CACHE_T *bp; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ bp = p_fs->buf_cache_lru_list.prev; | |
+ while (bp->flag & LOCKBIT) bp = bp->prev; | |
+ | |
+ | |
+ move_to_mru(bp, &p_fs->buf_cache_lru_list); | |
+ return(bp); | |
+} /* end of buf_cache_get */ | |
+ | |
+static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) | |
+{ | |
+ INT32 off; | |
+ BUF_CACHE_T *hp; | |
+ FS_INFO_T *p_fs; | |
+ | |
+ p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE-1); | |
+ | |
+ hp = &(p_fs->buf_cache_hash_list[off]); | |
+ bp->hash_next = hp->hash_next; | |
+ bp->hash_prev = hp; | |
+ hp->hash_next->hash_prev = bp; | |
+ hp->hash_next = bp; | |
+} /* 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 */ | |
+ | |
+/* end of exfat_cache.c */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_cache.h exfat_kernel-2.6.32/fs/exfat/exfat_cache.h | |
--- kernel-2.6.32/fs/exfat/exfat_cache.h 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_cache.h 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,94 @@ | |
+/* | |
+ * 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 "exfat_config.h" | |
+#include "exfat_global.h" | |
+ | |
+#ifdef __cplusplus | |
+extern "C" { | |
+#endif /* __cplusplus */ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* 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; | |
+ INT32 drv; | |
+ UINT32 sec; | |
+ UINT32 flag; | |
+ struct buffer_head *buf_bh; | |
+ } BUF_CACHE_T; | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* External Function Declarations */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ INT32 buf_init(struct super_block *sb); | |
+ INT32 buf_shutdown(struct super_block *sb); | |
+ INT32 FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content); | |
+ INT32 FAT_write(struct super_block *sb, UINT32 loc, UINT32 content); | |
+ UINT8 *FAT_getblk(struct super_block *sb, UINT32 sec); | |
+ void FAT_modify(struct super_block *sb, UINT32 sec); | |
+ void FAT_release_all(struct super_block *sb); | |
+ void FAT_sync(struct super_block *sb); | |
+ UINT8 *buf_getblk(struct super_block *sb, UINT32 sec); | |
+ void buf_modify(struct super_block *sb, UINT32 sec); | |
+ void buf_lock(struct super_block *sb, UINT32 sec); | |
+ void buf_unlock(struct super_block *sb, UINT32 sec); | |
+ void buf_release(struct super_block *sb, UINT32 sec); | |
+ void buf_release_all(struct super_block *sb); | |
+ void buf_sync(struct super_block *sb); | |
+ | |
+#ifdef __cplusplus | |
+} | |
+#endif /* __cplusplus */ | |
+ | |
+#endif /* _EXFAT_CACHE_H */ | |
+ | |
+/* end of exfat_cache.h */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_config.h exfat_kernel-2.6.32/fs/exfat/exfat_config.h | |
--- kernel-2.6.32/fs/exfat/exfat_config.h 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_config.h 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,102 @@ | |
+/* | |
+ * 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 | |
+ | |
+#ifdef __cplusplus | |
+extern "C" { | |
+#endif /* __cplusplus */ | |
+ | |
+/*======================================================================*/ | |
+/* */ | |
+/* FFS CONFIGURATIONS */ | |
+/* (CHANGE THIS PART IF REQUIRED) */ | |
+/* */ | |
+/*======================================================================*/ | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Target OS Platform */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+#define OS_NONOS 1 | |
+#define OS_LINUX 2 | |
+ | |
+#define FFS_CONFIG_OS OS_LINUX | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Set this definition to 1 to support APIs with pointer parameters */ | |
+/* to 32-bit variables (e.g. read, write, seek, get_filesize) */ | |
+/*----------------------------------------------------------------------*/ | |
+#define FFS_CONFIG_LEGACY_32BIT_API 0 | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Set this definition to 1 to support APIs with pointer parameters */ | |
+/* to 32-bit variables (e.g. read, write, seek, get_filesize) */ | |
+/*----------------------------------------------------------------------*/ | |
+#define FFS_CONFIG_LEGACY_32BIT_API 0 | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Set appropriate definitions to 1's to support the languages */ | |
+/*----------------------------------------------------------------------*/ | |
+#define FFS_CONFIG_SUPPORT_CP1250 1 // Central Europe | |
+#define FFS_CONFIG_SUPPORT_CP1251 1 // Cyrillic | |
+#define FFS_CONFIG_SUPPORT_CP1252 1 // Latin I | |
+#define FFS_CONFIG_SUPPORT_CP1253 1 // Greek | |
+#define FFS_CONFIG_SUPPORT_CP1254 1 // Turkish | |
+#define FFS_CONFIG_SUPPORT_CP1255 1 // Hebrew | |
+#define FFS_CONFIG_SUPPORT_CP1256 1 // Arabic | |
+#define FFS_CONFIG_SUPPORT_CP1257 1 // Baltic | |
+#define FFS_CONFIG_SUPPORT_CP1258 1 // Vietnamese | |
+#define FFS_CONFIG_SUPPORT_CP874 1 // Thai | |
+#define FFS_CONFIG_SUPPORT_CP932 1 // Japanese | |
+#define FFS_CONFIG_SUPPORT_CP936 1 // Simplified Chinese | |
+#define FFS_CONFIG_SUPPORT_CP949 1 // Korean | |
+#define FFS_CONFIG_SUPPORT_CP950 1 // Traditional Chinese | |
+#define FFS_CONFIG_SUPPORT_UTF8 1 // UTF8 encoding | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Feature Config */ | |
+/*----------------------------------------------------------------------*/ | |
+#define EXFAT_CONFIG_DISCARD 1 // mount option -o discard support | |
+#define EXFAT_CONFIG_KERNEL_DEBUG 1 // kernel debug features via ioctl | |
+#define EXFAT_CONFIG_DEBUG_MSG 0 // debugging message on/off | |
+ | |
+#ifdef __cplusplus | |
+} | |
+#endif /* __cplusplus */ | |
+ | |
+#endif /* _EXFAT_CONFIG_H */ | |
+ | |
+/* end of exfat_config.h */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_core.c exfat_kernel-2.6.32/fs/exfat/exfat_core.c | |
--- kernel-2.6.32/fs/exfat/exfat_core.c 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_core.c 2013-09-01 13:03:29.000000000 +1100 | |
@@ -0,0 +1,5187 @@ | |
+/* 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.c */ | |
+/* PURPOSE : exFAT File Manager */ | |
+/* */ | |
+/*----------------------------------------------------------------------*/ | |
+/* NOTES */ | |
+/* */ | |
+/*----------------------------------------------------------------------*/ | |
+/* REVISION HISTORY (Ver 0.9) */ | |
+/* */ | |
+/* - 2010.11.15 [Joosun Hahn] : first writing */ | |
+/* */ | |
+/************************************************************************/ | |
+ | |
+#include <linux/version.h> | |
+ | |
+#include "exfat_config.h" | |
+#include "exfat_global.h" | |
+#include "exfat_data.h" | |
+#include "exfat_oal.h" | |
+ | |
+#include "exfat_blkdev.h" | |
+#include "exfat_cache.h" | |
+#include "exfat_nls.h" | |
+#include "exfat_api.h" | |
+#include "exfat_super.h" | |
+#include "exfat.h" | |
+ | |
+#include <linux/blkdev.h> | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Constant & Macro Definitions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+#define THERE_IS_MBR 0 /* if there is no MBR (e.g. memory card), | |
+set this macro to 0 */ | |
+ | |
+#if (THERE_IS_MBR == 1) | |
+#include "exfat_part.h" | |
+#endif | |
+ | |
+#define DELAYED_SYNC 0 | |
+ | |
+#define ELAPSED_TIME 0 | |
+ | |
+#if (ELAPSED_TIME == 1) | |
+#include <linux/time.h> | |
+ | |
+static UINT32 __t1, __t2; | |
+static UINT32 get_current_msec(void) | |
+{ | |
+ struct timeval tm; | |
+ do_gettimeofday(&tm); | |
+ return (UINT32)(tm.tv_sec*1000000 + tm.tv_usec); | |
+} | |
+#define TIME_START() do {__t1 = get_current_msec(); } while (0) | |
+#define TIME_END() do {__t2 = get_current_msec(); } while (0) | |
+#define PRINT_TIME(n) do {printk("[EXFAT] Elapsed time %d = %d (usec)\n", n, (__t2 - __t1)); } while (0) | |
+#else | |
+#define TIME_START() | |
+#define TIME_END() | |
+#define PRINT_TIME(n) | |
+#endif | |
+ | |
+static void __set_sb_dirty(struct super_block *sb) | |
+{ | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) | |
+ sb->s_dirty = 1; | |
+#else | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ sbi->s_dirt = 1; | |
+#endif | |
+} | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Global Variable Definitions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+extern UINT8 uni_upcase[]; | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Local Variable Definitions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+static UINT8 name_buf[MAX_PATH_LENGTH *MAX_CHARSET_SIZE]; | |
+ | |
+static INT8 *reserved_names[] = { | |
+ "AUX ", "CON ", "NUL ", "PRN ", | |
+ "COM1 ", "COM2 ", "COM3 ", "COM4 ", | |
+ "COM5 ", "COM6 ", "COM7 ", "COM8 ", "COM9 ", | |
+ "LPT1 ", "LPT2 ", "LPT3 ", "LPT4 ", | |
+ "LPT5 ", "LPT6 ", "LPT7 ", "LPT8 ", "LPT9 ", | |
+ NULL | |
+}; | |
+ | |
+static UINT8 free_bit[] = { | |
+ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, /* 0 ~ 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 UINT8 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 */ | |
+INT32 ffsInit(void) | |
+{ | |
+ INT32 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 */ | |
+INT32 ffsShutdown(void) | |
+{ | |
+ INT32 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 */ | |
+INT32 ffsMountVol(struct super_block *sb, INT32 drv) | |
+{ | |
+ INT32 i, ret; | |
+#if (THERE_IS_MBR == 1) | |
+ MBR_SECTOR_T *p_mbr; | |
+ PART_ENTRY_T *p_pte; | |
+#endif | |
+ PBR_SECTOR_T *p_pbr; | |
+ struct buffer_head *tmp_bh = NULL; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ PRINTK("[EXFAT] trying to mount...\n"); | |
+ | |
+ p_fs->drv = drv; | |
+ p_fs->dev_ejected = FALSE; | |
+ | |
+ /* 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; | |
+ | |
+#if (THERE_IS_MBR == 1) | |
+ if (buf[0] != 0xEB) { | |
+ /* MBR is read */ | |
+ p_mbr = (MBR_SECTOR_T *) tmp_bh->b_data; | |
+ | |
+ /* check the validity of MBR */ | |
+ if (GET16_A(p_mbr->signature) != MBR_SIGNATURE) { | |
+ brelse(tmp_bh); | |
+ bdev_close(sb); | |
+ return FFS_FORMATERR; | |
+ } | |
+ | |
+ p_pte = (PART_ENTRY_T *) p_mbr->partition + 0; | |
+ p_fs->PBR_sector = GET32(p_pte->start_sector); | |
+ p_fs->num_sectors = GET32(p_pte->num_sectors); | |
+ | |
+ if (p_fs->num_sectors == 0) { | |
+ brelse(tmp_bh); | |
+ bdev_close(sb); | |
+ return FFS_ERROR; | |
+ } | |
+ | |
+ /* read PBR */ | |
+ if (sector_read(sb, p_fs->PBR_sector, &tmp_bh, 1) != FFS_SUCCESS) { | |
+ bdev_close(sb); | |
+ return FFS_MEDIAERR; | |
+ } | |
+ } else { | |
+#endif | |
+ /* PRB is read */ | |
+ p_fs->PBR_sector = 0; | |
+#if (THERE_IS_MBR == 1) | |
+ } | |
+#endif | |
+ | |
+ 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 */ | |
+INT32 ffsUmountVol(struct super_block *sb) | |
+{ | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ PRINTK("[EXFAT] trying to unmount...\n"); | |
+ | |
+ fs_sync(sb, 0); | |
+ fs_set_vol_flags(sb, VOL_CLEAN); | |
+ | |
+ if (p_fs->vol_type == EXFAT) { | |
+ free_upcase_table(sb); | |
+ free_alloc_bitmap(sb); | |
+ } | |
+ | |
+ FAT_release_all(sb); | |
+ buf_release_all(sb); | |
+ | |
+ /* 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 */ | |
+INT32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info) | |
+{ | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if (p_fs->used_clusters == (UINT32) ~0) | |
+ p_fs->used_clusters = p_fs->fs_func->count_used_clusters(sb); | |
+ | |
+ info->FatType = p_fs->vol_type; | |
+ info->ClusterSize = p_fs->cluster_size; | |
+ info->NumClusters = p_fs->num_clusters - 2; /* 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 */ | |
+INT32 ffsSyncVol(struct super_block *sb, INT32 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 */ | |
+INT32 ffsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid) | |
+{ | |
+ INT32 ret, dentry, num_entries; | |
+ CHAIN_T dir; | |
+ UNI_NAME_T uni_name; | |
+ DOS_NAME_T dos_name; | |
+ DENTRY_T *ep, *ep2; | |
+ ENTRY_SET_CACHE_T *es=NULL; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ PRINTK("ffsLookupFile entered\n"); | |
+ | |
+ /* 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; | |
+ | |
+ PRINTK("ffsLookupFile exited successfully\n"); | |
+ | |
+ return FFS_SUCCESS; | |
+} /* end of ffsLookupFile */ | |
+ | |
+/* ffsCreateFile : create a file */ | |
+INT32 ffsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid) | |
+{ | |
+ INT32 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); | |
+ | |
+#if (DELAYED_SYNC == 0) | |
+ fs_sync(sb, 0); | |
+ fs_set_vol_flags(sb, VOL_CLEAN); | |
+#endif | |
+ | |
+ if (p_fs->dev_ejected) | |
+ return FFS_MEDIAERR; | |
+ | |
+ return ret; | |
+} /* end of ffsCreateFile */ | |
+ | |
+/* ffsReadFile : read data from a opened file */ | |
+INT32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount) | |
+{ | |
+ INT32 offset, sec_offset, clu_offset; | |
+ UINT32 clu, LogSector; | |
+ UINT64 oneblkread, read_bytes; | |
+ struct buffer_head *tmp_bh = NULL; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ /* 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 = (INT32)(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 = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); | |
+ fid->hint_last_clu = clu; | |
+ | |
+ offset = (INT32)(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 = (UINT64)(p_bd->sector_size - offset); | |
+ if (oneblkread > count) | |
+ oneblkread = count; | |
+ | |
+ if ((offset == 0) && (oneblkread == p_bd->sector_size)) { | |
+ if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) | |
+ goto err_out; | |
+ MEMCPY(((INT8 *) buffer)+read_bytes, ((INT8 *) tmp_bh->b_data), (INT32) oneblkread); | |
+ } else { | |
+ if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) | |
+ goto err_out; | |
+ MEMCPY(((INT8 *) buffer)+read_bytes, ((INT8 *) tmp_bh->b_data)+offset, (INT32) oneblkread); | |
+ } | |
+ count -= oneblkread; | |
+ read_bytes += oneblkread; | |
+ fid->rwoffset += oneblkread; | |
+ } | |
+ brelse(tmp_bh); | |
+ | |
+err_out: | |
+ /* 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 */ | |
+INT32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount) | |
+{ | |
+ INT32 modified = FALSE, offset, sec_offset, clu_offset; | |
+ INT32 num_clusters, num_alloc, num_alloced = (INT32) ~0; | |
+ UINT32 clu, last_clu, LogSector, sector = 0; | |
+ UINT64 oneblkwrite, write_bytes; | |
+ CHAIN_T new_clu; | |
+ TIMESTAMP_T tm; | |
+ DENTRY_T *ep, *ep2; | |
+ ENTRY_SET_CACHE_T *es = NULL; | |
+ struct buffer_head *tmp_bh = NULL; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ /* 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 = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; | |
+ | |
+ write_bytes = 0; | |
+ | |
+ while (count > 0) { | |
+ clu_offset = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); | |
+ clu = last_clu = fid->start_clu; | |
+ | |
+ if (fid->flags == 0x03) { | |
+ if ((clu_offset > 0) && (clu != CLUSTER_32(~0))) { | |
+ last_clu += clu_offset - 1; | |
+ | |
+ if (clu_offset == num_clusters) | |
+ clu = CLUSTER_32(~0); | |
+ else | |
+ clu += clu_offset; | |
+ } | |
+ } else { | |
+ /* 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 = (INT32)((count-1) >> p_fs->cluster_size_bits) + 1; | |
+ new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) : last_clu+1; | |
+ new_clu.size = 0; | |
+ new_clu.flags = fid->flags; | |
+ | |
+ /* (1) allocate a chain of clusters */ | |
+ num_alloced = p_fs->fs_func->alloc_cluster(sb, num_alloc, &new_clu); | |
+ if (num_alloced == 0) | |
+ break; | |
+ | |
+ /* (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 = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); | |
+ fid->hint_last_clu = clu; | |
+ | |
+ offset = (INT32)(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 = (UINT64)(p_bd->sector_size - offset); | |
+ if (oneblkwrite > count) | |
+ oneblkwrite = count; | |
+ | |
+ if ((offset == 0) && (oneblkwrite == p_bd->sector_size)) { | |
+ if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS) | |
+ goto err_out; | |
+ MEMCPY(((INT8 *) tmp_bh->b_data), ((INT8 *) buffer)+write_bytes, (INT32) oneblkwrite); | |
+ if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) { | |
+ brelse(tmp_bh); | |
+ goto err_out; | |
+ } | |
+ } else { | |
+ if ((offset > 0) || ((fid->rwoffset+oneblkwrite) < fid->size)) { | |
+ if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) | |
+ goto err_out; | |
+ } else { | |
+ if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS) | |
+ goto err_out; | |
+ } | |
+ | |
+ MEMCPY(((INT8 *) tmp_bh->b_data)+offset, ((INT8 *) buffer)+write_bytes, (INT32) oneblkwrite); | |
+ if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) { | |
+ brelse(tmp_bh); | |
+ goto err_out; | |
+ } | |
+ } | |
+ | |
+ count -= oneblkwrite; | |
+ write_bytes += oneblkwrite; | |
+ fid->rwoffset += oneblkwrite; | |
+ | |
+ fid->attr |= ATTR_ARCHIVE; | |
+ | |
+ if (fid->size < fid->rwoffset) { | |
+ fid->size = fid->rwoffset; | |
+ modified = TRUE; | |
+ } | |
+ } | |
+ | |
+ brelse(tmp_bh); | |
+ | |
+ /* (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); | |
+ } | |
+ | |
+#if (DELAYED_SYNC == 0) | |
+ 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 */ | |
+INT32 ffsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size) | |
+{ | |
+ INT32 num_clusters; | |
+ UINT32 last_clu = CLUSTER_32(0), 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 = (INT32)((old_size-1) >> p_fs->cluster_size_bits) + 1; | |
+ clu.flags = fid->flags; | |
+ | |
+ if (new_size > 0) { | |
+ num_clusters = (INT32)((new_size-1) >> p_fs->cluster_size_bits) + 1; | |
+ | |
+ if (clu.flags == 0x03) { | |
+ clu.dir += num_clusters; | |
+ } else { | |
+ while (num_clusters > 0) { | |
+ last_clu = clu.dir; | |
+ if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) | |
+ return FFS_MEDIAERR; | |
+ num_clusters--; | |
+ } | |
+ } | |
+ | |
+ clu.size -= num_clusters; | |
+ } | |
+ | |
+ fid->size = new_size; | |
+ fid->attr |= ATTR_ARCHIVE; | |
+ if (new_size == 0) { | |
+ fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; | |
+ fid->start_clu = CLUSTER_32(~0); | |
+ } | |
+ | |
+ /* (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; | |
+ } | |
+ | |
+#if (DELAYED_SYNC == 0) | |
+ fs_sync(sb, 0); | |
+ fs_set_vol_flags(sb, VOL_CLEAN); | |
+#endif | |
+ | |
+ if (p_fs->dev_ejected) | |
+ return FFS_MEDIAERR; | |
+ | |
+ return FFS_SUCCESS; | |
+} /* end of ffsTruncateFile */ | |
+ | |
+static void update_parent_info( FILE_ID_T *fid, struct inode *parent_inode) | |
+{ | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(parent_inode->i_sb)->fs_info); | |
+ FILE_ID_T *parent_fid = &(EXFAT_I(parent_inode)->fid); | |
+ | |
+ if (unlikely((parent_fid->flags != fid->dir.flags) | |
+ || (parent_fid->size != (fid->dir.size<<p_fs->cluster_size_bits)) | |
+ || (parent_fid->start_clu != fid->dir.dir))) { | |
+ | |
+ fid->dir.dir = parent_fid->start_clu; | |
+ fid->dir.flags = parent_fid->flags; | |
+ fid->dir.size = ((parent_fid->size + (p_fs->cluster_size-1)) | |
+ >> p_fs->cluster_size_bits); | |
+ } | |
+} | |
+ | |
+/* ffsMoveFile : move(rename) a old file into a new file */ | |
+INT32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) | |
+{ | |
+ INT32 ret; | |
+ INT32 dentry; | |
+ CHAIN_T olddir, newdir; | |
+ CHAIN_T *p_dir=NULL; | |
+ UNI_NAME_T uni_name; | |
+ DENTRY_T *ep; | |
+ struct super_block *sb = old_parent_inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ UINT8 *new_path = (UINT8 *) new_dentry->d_name.name; | |
+ struct inode *new_inode = new_dentry->d_inode; | |
+ int num_entries; | |
+ FILE_ID_T *new_fid = NULL; | |
+ INT32 new_entry=0; | |
+ | |
+ /* 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) { | |
+ UINT32 entry_type; | |
+ | |
+ ret = FFS_MEDIAERR; | |
+ new_fid = &EXFAT_I(new_inode)->fid; | |
+ | |
+ update_parent_info(new_fid, new_parent_inode); | |
+ | |
+ p_dir = &(new_fid->dir); | |
+ new_entry = new_fid->entry; | |
+ ep = get_entry_in_dir(sb, p_dir, new_entry, NULL); | |
+ if (!ep) | |
+ goto out; | |
+ | |
+ entry_type = p_fs->fs_func->get_entry_type(ep); | |
+ | |
+ if (entry_type == TYPE_DIR) { | |
+ CHAIN_T new_clu; | |
+ new_clu.dir = new_fid->start_clu; | |
+ new_clu.size = (INT32)((new_fid->size-1) >> p_fs->cluster_size_bits) + 1; | |
+ new_clu.flags = new_fid->flags; | |
+ | |
+ if (!is_dir_empty(sb, &new_clu)) | |
+ return FFS_FILEEXIST; | |
+ } | |
+ } | |
+ | |
+ /* 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: | |
+#if (DELAYED_SYNC == 0) | |
+ fs_sync(sb, 0); | |
+ fs_set_vol_flags(sb, VOL_CLEAN); | |
+#endif | |
+ | |
+ if (p_fs->dev_ejected) | |
+ return FFS_MEDIAERR; | |
+ | |
+ return ret; | |
+} /* end of ffsMoveFile */ | |
+ | |
+/* ffsRemoveFile : remove a file */ | |
+INT32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid) | |
+{ | |
+ INT32 dentry; | |
+ CHAIN_T dir, clu_to_free; | |
+ DENTRY_T *ep; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ dir.dir = fid->dir.dir; | |
+ dir.size = fid->dir.size; | |
+ dir.flags = fid->dir.flags; | |
+ | |
+ dentry = fid->entry; | |
+ | |
+ ep = get_entry_in_dir(sb, &dir, dentry, NULL); | |
+ if (!ep) | |
+ return FFS_MEDIAERR; | |
+ | |
+ if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY) | |
+ return FFS_PERMISSIONERR; | |
+ | |
+ fs_set_vol_flags(sb, VOL_DIRTY); | |
+ | |
+ /* (1) update the directory entry */ | |
+ remove_file(inode, &dir, dentry); | |
+ | |
+ clu_to_free.dir = fid->start_clu; | |
+ clu_to_free.size = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; | |
+ clu_to_free.flags = fid->flags; | |
+ | |
+ /* (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; | |
+ | |
+#if (DELAYED_SYNC == 0) | |
+ fs_sync(sb, 0); | |
+ fs_set_vol_flags(sb, VOL_CLEAN); | |
+#endif | |
+ | |
+ if (p_fs->dev_ejected) | |
+ return FFS_MEDIAERR; | |
+ | |
+ return FFS_SUCCESS; | |
+} /* end of ffsRemoveFile */ | |
+ | |
+/* ffsSetAttr : set the attribute of a given file */ | |
+INT32 ffsSetAttr(struct inode *inode, UINT32 attr) | |
+{ | |
+ UINT32 type, 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); | |
+ UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; | |
+ ENTRY_SET_CACHE_T *es = NULL; | |
+ | |
+ if (fid->attr == attr) { | |
+ if (p_fs->dev_ejected) | |
+ return FFS_MEDIAERR; | |
+ return FFS_SUCCESS; | |
+ } | |
+ | |
+ if (is_dir) { | |
+ if ((fid->dir.dir == p_fs->root_dir) && | |
+ (fid->entry == -1)) { | |
+ if (p_fs->dev_ejected) | |
+ return FFS_MEDIAERR; | |
+ return FFS_SUCCESS; | |
+ } | |
+ } | |
+ | |
+ /* 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)))) { | |
+ INT32 err; | |
+ if (p_fs->dev_ejected) | |
+ err = FFS_MEDIAERR; | |
+ else | |
+ err = FFS_ERROR; | |
+ | |
+ if (p_fs->vol_type == EXFAT) | |
+ release_entry_set(es); | |
+ return err; | |
+ } | |
+ | |
+ fs_set_vol_flags(sb, VOL_DIRTY); | |
+ | |
+ /* 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); | |
+ } | |
+ | |
+#if (DELAYED_SYNC == 0) | |
+ fs_sync(sb, 0); | |
+ fs_set_vol_flags(sb, VOL_CLEAN); | |
+#endif | |
+ | |
+ if (p_fs->dev_ejected) | |
+ return FFS_MEDIAERR; | |
+ | |
+ return FFS_SUCCESS; | |
+} /* end of ffsSetAttr */ | |
+ | |
+/* ffsGetStat : get the information of a given file */ | |
+INT32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info) | |
+{ | |
+ UINT32 sector = 0; | |
+ INT32 count; | |
+ CHAIN_T dir; | |
+ UNI_NAME_T uni_name; | |
+ TIMESTAMP_T tm; | |
+ DENTRY_T *ep, *ep2; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); | |
+ ENTRY_SET_CACHE_T *es=NULL; | |
+ UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; | |
+ | |
+ PRINTK("ffsGetStat entered\n"); | |
+ | |
+ if (is_dir) { | |
+ if ((fid->dir.dir == p_fs->root_dir) && | |
+ (fid->entry == -1)) { | |
+ info->Attr = ATTR_SUBDIR; | |
+ MEMSET((INT8 *) &info->CreateTimestamp, 0, sizeof(DATE_TIME_T)); | |
+ MEMSET((INT8 *) &info->ModifyTimestamp, 0, sizeof(DATE_TIME_T)); | |
+ MEMSET((INT8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); | |
+ STRCPY(info->ShortName, "."); | |
+ STRCPY(info->Name, "."); | |
+ | |
+ dir.dir = p_fs->root_dir; | |
+ dir.flags = 0x01; | |
+ | |
+ if (p_fs->root_dir == CLUSTER_32(0)) /* 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((INT8 *) &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) | |
+ get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); | |
+ nls_uniname_to_cstring(sb, info->Name, &uni_name); | |
+ | |
+ if (p_fs->vol_type == EXFAT) { | |
+ info->NumSubdirs = 2; | |
+ } else { | |
+ buf_unlock(sb, sector); | |
+ get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0); | |
+ nls_uniname_to_cstring(sb, info->ShortName, &uni_name); | |
+ info->NumSubdirs = 0; | |
+ } | |
+ | |
+ info->Size = p_fs->fs_func->get_entry_size(ep2); | |
+ | |
+ if (p_fs->vol_type == EXFAT) | |
+ release_entry_set(es); | |
+ | |
+ if (is_dir) { | |
+ dir.dir = fid->start_clu; | |
+ dir.flags = 0x01; | |
+ | |
+ if (info->Size == 0) | |
+ info->Size = (UINT64) count_num_clusters(sb, &dir) << p_fs->cluster_size_bits; | |
+ | |
+ count = count_dos_name_entries(sb, &dir, TYPE_DIR); | |
+ if (count < 0) | |
+ return FFS_MEDIAERR; | |
+ info->NumSubdirs += count; | |
+ } | |
+ | |
+ if (p_fs->dev_ejected) | |
+ return FFS_MEDIAERR; | |
+ | |
+ PRINTK("ffsGetStat exited successfully\n"); | |
+ return FFS_SUCCESS; | |
+} /* end of ffsGetStat */ | |
+ | |
+/* ffsSetStat : set the information of a given file */ | |
+INT32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info) | |
+{ | |
+ UINT32 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); | |
+ UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; | |
+ | |
+ if (is_dir) { | |
+ if ((fid->dir.dir == p_fs->root_dir) && | |
+ (fid->entry == -1)) { | |
+ if (p_fs->dev_ejected) | |
+ return FFS_MEDIAERR; | |
+ return FFS_SUCCESS; | |
+ } | |
+ } | |
+ | |
+ fs_set_vol_flags(sb, VOL_DIRTY); | |
+ | |
+ /* 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 */ | |
+ | |
+INT32 ffsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu) | |
+{ | |
+ INT32 num_clusters, num_alloced, modified = FALSE; | |
+ UINT32 last_clu, 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 = (INT64)(clu_offset) << p_fs->cluster_size_bits; | |
+ | |
+ if (EXFAT_I(inode)->mmu_private == 0) | |
+ num_clusters = 0; | |
+ else | |
+ num_clusters = (INT32)((EXFAT_I(inode)->mmu_private-1) >> p_fs->cluster_size_bits) + 1; | |
+ | |
+ *clu = last_clu = fid->start_clu; | |
+ | |
+ if (fid->flags == 0x03) { | |
+ if ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) { | |
+ last_clu += clu_offset - 1; | |
+ | |
+ if (clu_offset == num_clusters) | |
+ *clu = CLUSTER_32(~0); | |
+ else | |
+ *clu += clu_offset; | |
+ } | |
+ } else { | |
+ /* 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 < 1) | |
+ 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); | |
+ } | |
+ | |
+ *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 = (INT32)(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 */ | |
+INT32 ffsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid) | |
+{ | |
+ INT32 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); | |
+ | |
+ PRINTK("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); | |
+ | |
+#if (DELAYED_SYNC == 0) | |
+ fs_sync(sb, 0); | |
+ fs_set_vol_flags(sb, VOL_CLEAN); | |
+#endif | |
+ | |
+ if (p_fs->dev_ejected) | |
+ return FFS_MEDIAERR; | |
+ | |
+ return ret; | |
+} /* end of ffsCreateDir */ | |
+ | |
+/* ffsReadDir : read a directory entry from the opened directory */ | |
+INT32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) | |
+{ | |
+ INT32 i, dentry, clu_offset; | |
+ INT32 dentries_per_clu, dentries_per_clu_bits = 0; | |
+ UINT32 type, sector; | |
+ CHAIN_T dir, clu; | |
+ UNI_NAME_T uni_name; | |
+ TIMESTAMP_T tm; | |
+ DENTRY_T *ep; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); | |
+ | |
+ /* 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 = (INT32)(fid->size >> p_fs->cluster_size_bits); | |
+ dir.flags = fid->flags; | |
+ } | |
+ | |
+ dentry = (INT32) 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 = my_log2(dentries_per_clu); | |
+ | |
+ clu_offset = dentry >> dentries_per_clu_bits; | |
+ clu.dir = dir.dir; | |
+ clu.size = dir.size; | |
+ clu.flags = dir.flags; | |
+ | |
+ if (clu.flags == 0x03) { | |
+ clu.dir += clu_offset; | |
+ clu.size -= clu_offset; | |
+ } else { | |
+ /* 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((INT8 *) &dir_entry->AccessTimestamp, 0, sizeof(DATE_TIME_T)); | |
+ | |
+ *(uni_name.name) = 0x0; | |
+ p_fs->fs_func->get_uni_name_from_ext_entry(sb, &dir, dentry, uni_name.name); | |
+ if (*(uni_name.name) == 0x0) | |
+ get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); | |
+ nls_uniname_to_cstring(sb, dir_entry->Name, &uni_name); | |
+ buf_unlock(sb, sector); | |
+ | |
+ if (p_fs->vol_type == EXFAT) { | |
+ ep = get_entry_in_dir(sb, &clu, i+1, NULL); | |
+ if (!ep) | |
+ return FFS_MEDIAERR; | |
+ } else { | |
+ get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0); | |
+ nls_uniname_to_cstring(sb, dir_entry->ShortName, &uni_name); | |
+ } | |
+ | |
+ dir_entry->Size = p_fs->fs_func->get_entry_size(ep); | |
+ | |
+ /* 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 = (INT64) ++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 = (INT64) ++dentry; | |
+ | |
+ if (p_fs->dev_ejected) | |
+ return FFS_MEDIAERR; | |
+ | |
+ return FFS_SUCCESS; | |
+} /* end of ffsReadDir */ | |
+ | |
+/* ffsRemoveDir : remove a directory */ | |
+INT32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid) | |
+{ | |
+ INT32 dentry; | |
+ CHAIN_T dir, clu_to_free; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ dir.dir = fid->dir.dir; | |
+ dir.size = fid->dir.size; | |
+ dir.flags = fid->dir.flags; | |
+ | |
+ dentry = fid->entry; | |
+ | |
+ /* 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 = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; | |
+ clu_to_free.flags = fid->flags; | |
+ | |
+ if (!is_dir_empty(sb, &clu_to_free)) | |
+ return FFS_FILEEXIST; | |
+ | |
+ fs_set_vol_flags(sb, VOL_DIRTY); | |
+ | |
+ /* (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; | |
+ | |
+#if (DELAYED_SYNC == 0) | |
+ fs_sync(sb, 0); | |
+ fs_set_vol_flags(sb, VOL_CLEAN); | |
+#endif | |
+ | |
+ if (p_fs->dev_ejected) | |
+ return FFS_MEDIAERR; | |
+ | |
+ return FFS_SUCCESS; | |
+} /* end of ffsRemoveDir */ | |
+ | |
+/*======================================================================*/ | |
+/* Local Function Definitions */ | |
+/*======================================================================*/ | |
+ | |
+/* | |
+ * File System Management Functions | |
+ */ | |
+ | |
+INT32 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 */ | |
+ | |
+INT32 fs_shutdown(void) | |
+{ | |
+ return FFS_SUCCESS; | |
+} /* end of fs_shutdown */ | |
+ | |
+void fs_set_vol_flags(struct super_block *sb, UINT32 new_flag) | |
+{ | |
+ PBR_SECTOR_T *p_pbr; | |
+ BPBEX_T *p_bpb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if (p_fs->vol_flag == new_flag) | |
+ return; | |
+ | |
+ p_fs->vol_flag = new_flag; | |
+ | |
+ if (p_fs->vol_type == EXFAT) { | |
+ if (p_fs->pbr_bh == NULL) { | |
+ if (sector_read(sb, p_fs->PBR_sector, &(p_fs->pbr_bh), 1) != FFS_SUCCESS) | |
+ return; | |
+ } | |
+ | |
+ p_pbr = (PBR_SECTOR_T *) p_fs->pbr_bh->b_data; | |
+ p_bpb = (BPBEX_T *) p_pbr->bpb; | |
+ SET16(p_bpb->vol_flags, (UINT16) new_flag); | |
+ | |
+ /* 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, INT32 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 | |
+ */ | |
+ | |
+INT32 clear_cluster(struct super_block *sb, UINT32 clu) | |
+{ | |
+ UINT32 s, n; | |
+ INT32 ret = FFS_SUCCESS; | |
+ struct buffer_head *tmp_bh = NULL; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ if (clu == CLUSTER_32(0)) { /* 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++) { | |
+ if ((ret = sector_read(sb, s, &tmp_bh, 0)) != FFS_SUCCESS) | |
+ return ret; | |
+ | |
+ MEMSET((INT8 *) tmp_bh->b_data, 0x0, p_bd->sector_size); | |
+ if ((ret = sector_write(sb, s, tmp_bh, 0)) !=FFS_SUCCESS) | |
+ break; | |
+ } | |
+ | |
+ brelse(tmp_bh); | |
+ return ret; | |
+} /* end of clear_cluster */ | |
+ | |
+INT32 fat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain) | |
+{ | |
+ INT32 i, num_clusters = 0; | |
+ UINT32 new_clu, last_clu = CLUSTER_32(~0), read_clu; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ new_clu = p_chain->dir; | |
+ if (new_clu == CLUSTER_32(~0)) | |
+ new_clu = p_fs->clu_srch_ptr; | |
+ else if (new_clu >= p_fs->num_clusters) | |
+ new_clu = 2; | |
+ | |
+ __set_sb_dirty(sb); | |
+ | |
+ p_chain->dir = CLUSTER_32(~0); | |
+ | |
+ for (i = 2; i < p_fs->num_clusters; i++) { | |
+ if (FAT_read(sb, new_clu, &read_clu) != 0) | |
+ return 0; | |
+ | |
+ if (read_clu == CLUSTER_32(0)) { | |
+ FAT_write(sb, new_clu, CLUSTER_32(~0)); | |
+ num_clusters++; | |
+ | |
+ if (p_chain->dir == CLUSTER_32(~0)) | |
+ p_chain->dir = new_clu; | |
+ else | |
+ FAT_write(sb, last_clu, new_clu); | |
+ | |
+ last_clu = new_clu; | |
+ | |
+ if ((--num_alloc) == 0) { | |
+ p_fs->clu_srch_ptr = new_clu; | |
+ if (p_fs->used_clusters != (UINT32) ~0) | |
+ p_fs->used_clusters += num_clusters; | |
+ | |
+ return(num_clusters); | |
+ } | |
+ } | |
+ if ((++new_clu) >= p_fs->num_clusters) | |
+ new_clu = 2; | |
+ } | |
+ | |
+ p_fs->clu_srch_ptr = new_clu; | |
+ if (p_fs->used_clusters != (UINT32) ~0) | |
+ p_fs->used_clusters += num_clusters; | |
+ | |
+ return(num_clusters); | |
+} /* end of fat_alloc_cluster */ | |
+ | |
+INT32 exfat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain) | |
+{ | |
+ INT32 num_clusters = 0; | |
+ UINT32 hint_clu, new_clu, last_clu = CLUSTER_32(~0); | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ hint_clu = p_chain->dir; | |
+ if (hint_clu == CLUSTER_32(~0)) { | |
+ hint_clu = test_alloc_bitmap(sb, p_fs->clu_srch_ptr-2); | |
+ if (hint_clu == CLUSTER_32(~0)) | |
+ return 0; | |
+ } else if (hint_clu >= p_fs->num_clusters) { | |
+ hint_clu = 2; | |
+ p_chain->flags = 0x01; | |
+ } | |
+ | |
+ __set_sb_dirty(sb); | |
+ | |
+ p_chain->dir = CLUSTER_32(~0); | |
+ | |
+ while ((new_clu = test_alloc_bitmap(sb, hint_clu-2)) != CLUSTER_32(~0)) { | |
+ if (new_clu != hint_clu) { | |
+ if (p_chain->flags == 0x03) { | |
+ exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters); | |
+ p_chain->flags = 0x01; | |
+ } | |
+ } | |
+ | |
+ if (set_alloc_bitmap(sb, new_clu-2) != FFS_SUCCESS) | |
+ return 0; | |
+ | |
+ num_clusters++; | |
+ | |
+ if (p_chain->flags == 0x01) | |
+ FAT_write(sb, new_clu, CLUSTER_32(~0)); | |
+ | |
+ if (p_chain->dir == CLUSTER_32(~0)) { | |
+ p_chain->dir = new_clu; | |
+ } else { | |
+ if (p_chain->flags == 0x01) | |
+ FAT_write(sb, last_clu, new_clu); | |
+ } | |
+ last_clu = new_clu; | |
+ | |
+ if ((--num_alloc) == 0) { | |
+ p_fs->clu_srch_ptr = hint_clu; | |
+ if (p_fs->used_clusters != (UINT32) ~0) | |
+ p_fs->used_clusters += num_clusters; | |
+ | |
+ p_chain->size += num_clusters; | |
+ return(num_clusters); | |
+ } | |
+ | |
+ hint_clu = new_clu + 1; | |
+ if (hint_clu >= p_fs->num_clusters) { | |
+ hint_clu = 2; | |
+ | |
+ if (p_chain->flags == 0x03) { | |
+ exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters); | |
+ p_chain->flags = 0x01; | |
+ } | |
+ } | |
+ } | |
+ | |
+ p_fs->clu_srch_ptr = hint_clu; | |
+ if (p_fs->used_clusters != (UINT32) ~0) | |
+ p_fs->used_clusters += num_clusters; | |
+ | |
+ p_chain->size += num_clusters; | |
+ return(num_clusters); | |
+} /* end of exfat_alloc_cluster */ | |
+ | |
+void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse) | |
+{ | |
+ INT32 num_clusters = 0; | |
+ UINT32 clu, prev; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ INT32 i; | |
+ UINT32 sector; | |
+ | |
+ if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) | |
+ return; | |
+ __set_sb_dirty(sb); | |
+ clu = p_chain->dir; | |
+ | |
+ if (p_chain->size <= 0) | |
+ return; | |
+ | |
+ do { | |
+ if (p_fs->dev_ejected) | |
+ break; | |
+ | |
+ if (do_relse) { | |
+ sector = START_SECTOR(clu); | |
+ for (i = 0; i < p_fs->sectors_per_clu; i++) { | |
+ buf_release(sb, sector+i); | |
+ } | |
+ } | |
+ | |
+ prev = clu; | |
+ if (FAT_read(sb, clu, &clu) == -1) | |
+ break; | |
+ | |
+ FAT_write(sb, prev, CLUSTER_32(0)); | |
+ num_clusters++; | |
+ | |
+ } while (clu != CLUSTER_32(~0)); | |
+ | |
+ if (p_fs->used_clusters != (UINT32) ~0) | |
+ p_fs->used_clusters -= num_clusters; | |
+} /* end of fat_free_cluster */ | |
+ | |
+void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse) | |
+{ | |
+ INT32 num_clusters = 0; | |
+ UINT32 clu; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ INT32 i; | |
+ UINT32 sector; | |
+ | |
+ if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) | |
+ return; | |
+ | |
+ if (p_chain->size <= 0) { | |
+ printk(KERN_ERR "[EXFAT] free_cluster : skip free-req clu:%u, " | |
+ "because of zero-size truncation\n" | |
+ ,p_chain->dir); | |
+ return; | |
+ } | |
+ | |
+ __set_sb_dirty(sb); | |
+ clu = p_chain->dir; | |
+ | |
+ if (p_chain->flags == 0x03) { | |
+ do { | |
+ if (do_relse) { | |
+ sector = START_SECTOR(clu); | |
+ for (i = 0; i < p_fs->sectors_per_clu; i++) { | |
+ buf_release(sb, sector+i); | |
+ } | |
+ } | |
+ | |
+ if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS) | |
+ break; | |
+ clu++; | |
+ | |
+ num_clusters++; | |
+ } while (num_clusters < p_chain->size); | |
+ } else { | |
+ do { | |
+ if (p_fs->dev_ejected) | |
+ break; | |
+ | |
+ if (do_relse) { | |
+ sector = START_SECTOR(clu); | |
+ for (i = 0; i < p_fs->sectors_per_clu; i++) { | |
+ buf_release(sb, sector+i); | |
+ } | |
+ } | |
+ | |
+ if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS) | |
+ break; | |
+ | |
+ if (FAT_read(sb, clu, &clu) == -1) | |
+ break; | |
+ num_clusters++; | |
+ } while ((clu != CLUSTER_32(0)) && (clu != CLUSTER_32(~0))); | |
+ } | |
+ | |
+ if (p_fs->used_clusters != (UINT32) ~0) | |
+ p_fs->used_clusters -= num_clusters; | |
+} /* end of exfat_free_cluster */ | |
+ | |
+UINT32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain) | |
+{ | |
+ UINT32 clu, next; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ clu = p_chain->dir; | |
+ | |
+ if (p_chain->flags == 0x03) { | |
+ clu += p_chain->size - 1; | |
+ } else { | |
+ while((FAT_read(sb, clu, &next) == 0) && (next != CLUSTER_32(~0))) { | |
+ if (p_fs->dev_ejected) | |
+ break; | |
+ clu = next; | |
+ } | |
+ } | |
+ | |
+ return(clu); | |
+} /* end of find_last_cluster */ | |
+ | |
+INT32 count_num_clusters(struct super_block *sb, CHAIN_T *p_chain) | |
+{ | |
+ INT32 i, count = 0; | |
+ UINT32 clu; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) | |
+ return 0; | |
+ | |
+ clu = p_chain->dir; | |
+ | |
+ if (p_chain->flags == 0x03) { | |
+ count = p_chain->size; | |
+ } else { | |
+ for (i = 2; i < p_fs->num_clusters; i++) { | |
+ count++; | |
+ if (FAT_read(sb, clu, &clu) != 0) | |
+ return 0; | |
+ if (clu == CLUSTER_32(~0)) | |
+ break; | |
+ } | |
+ } | |
+ | |
+ return(count); | |
+} /* end of count_num_clusters */ | |
+ | |
+INT32 fat_count_used_clusters(struct super_block *sb) | |
+{ | |
+ INT32 i, count = 0; | |
+ UINT32 clu; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ for (i = 2; i < p_fs->num_clusters; i++) { | |
+ if (FAT_read(sb, i, &clu) != 0) | |
+ break; | |
+ if (clu != CLUSTER_32(0)) | |
+ count++; | |
+ } | |
+ | |
+ return(count); | |
+} /* end of fat_count_used_clusters */ | |
+ | |
+INT32 exfat_count_used_clusters(struct super_block *sb) | |
+{ | |
+ INT32 i, map_i, map_b, count = 0; | |
+ UINT8 k; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ map_i = map_b = 0; | |
+ | |
+ for (i = 2; i < p_fs->num_clusters; i += 8) { | |
+ k = *(((UINT8 *) p_fs->vol_amap[map_i]->b_data) + map_b); | |
+ count += used_bit[k]; | |
+ | |
+ if ((++map_b) >= p_bd->sector_size) { | |
+ map_i++; | |
+ map_b = 0; | |
+ } | |
+ } | |
+ | |
+ return(count); | |
+} /* end of exfat_count_used_clusters */ | |
+ | |
+void exfat_chain_cont_cluster(struct super_block *sb, UINT32 chain, INT32 len) | |
+{ | |
+ if (len == 0) | |
+ return; | |
+ | |
+ while (len > 1) { | |
+ FAT_write(sb, chain, chain+1); | |
+ chain++; | |
+ len--; | |
+ } | |
+ FAT_write(sb, chain, CLUSTER_32(~0)); | |
+} /* end of exfat_chain_cont_cluster */ | |
+ | |
+/* | |
+ * Allocation Bitmap Management Functions | |
+ */ | |
+ | |
+INT32 load_alloc_bitmap(struct super_block *sb) | |
+{ | |
+ INT32 i, j, ret; | |
+ UINT32 map_size; | |
+ UINT32 type, sector; | |
+ CHAIN_T clu; | |
+ BMAP_DENTRY_T *ep; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ clu.dir = p_fs->root_dir; | |
+ clu.flags = 0x01; | |
+ | |
+ while (clu.dir != CLUSTER_32(~0)) { | |
+ if (p_fs->dev_ejected) | |
+ break; | |
+ | |
+ for (i = 0; i < p_fs->dentries_per_clu; i++) { | |
+ ep = (BMAP_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); | |
+ if (!ep) | |
+ return FFS_MEDIAERR; | |
+ | |
+ type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); | |
+ | |
+ if (type == TYPE_UNUSED) | |
+ break; | |
+ if (type != TYPE_BITMAP) | |
+ continue; | |
+ | |
+ if (ep->flags == 0x0) { | |
+ p_fs->map_clu = GET32_A(ep->start_clu); | |
+ map_size = (UINT32) GET64_A(ep->size); | |
+ | |
+ p_fs->map_sectors = ((map_size-1) >> p_bd->sector_size_bits) + 1; | |
+ | |
+ p_fs->vol_amap = (struct buffer_head **) MALLOC(sizeof(struct buffer_head *) * p_fs->map_sectors); | |
+ if (p_fs->vol_amap == NULL) | |
+ return FFS_MEMORYERR; | |
+ | |
+ sector = START_SECTOR(p_fs->map_clu); | |
+ | |
+ for (j = 0; j < p_fs->map_sectors; j++) { | |
+ p_fs->vol_amap[j] = NULL; | |
+ ret = sector_read(sb, sector+j, &(p_fs->vol_amap[j]), 1); | |
+ if (ret != FFS_SUCCESS) { | |
+ /* release all buffers and free vol_amap */ | |
+ i=0; | |
+ while (i < j) | |
+ brelse(p_fs->vol_amap[i++]); | |
+ | |
+ FREE(p_fs->vol_amap); | |
+ p_fs->vol_amap = NULL; | |
+ return ret; | |
+ } | |
+ } | |
+ | |
+ p_fs->pbr_bh = NULL; | |
+ return FFS_SUCCESS; | |
+ } | |
+ } | |
+ | |
+ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) | |
+ return FFS_MEDIAERR; | |
+ } | |
+ | |
+ return FFS_FORMATERR; | |
+} /* end of load_alloc_bitmap */ | |
+ | |
+void free_alloc_bitmap(struct super_block *sb) | |
+{ | |
+ INT32 i; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ brelse(p_fs->pbr_bh); | |
+ | |
+ for (i = 0; i < p_fs->map_sectors; i++) { | |
+ __brelse(p_fs->vol_amap[i]); | |
+ } | |
+ | |
+ FREE(p_fs->vol_amap); | |
+ p_fs->vol_amap = NULL; | |
+} /* end of free_alloc_bitmap */ | |
+ | |
+INT32 set_alloc_bitmap(struct super_block *sb, UINT32 clu) | |
+{ | |
+ INT32 i, b; | |
+ UINT32 sector; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ i = clu >> (p_bd->sector_size_bits + 3); | |
+ b = clu & ((p_bd->sector_size << 3) - 1); | |
+ | |
+ sector = START_SECTOR(p_fs->map_clu) + i; | |
+ | |
+ Bitmap_set((UINT8 *) p_fs->vol_amap[i]->b_data, b); | |
+ | |
+ return (sector_write(sb, sector, p_fs->vol_amap[i], 0)); | |
+} /* end of set_alloc_bitmap */ | |
+ | |
+INT32 clr_alloc_bitmap(struct super_block *sb, UINT32 clu) | |
+{ | |
+ INT32 i, b; | |
+ UINT32 sector; | |
+#if EXFAT_CONFIG_DISCARD | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ struct exfat_mount_options *opts = &sbi->options; | |
+ int ret; | |
+#endif /* EXFAT_CONFIG_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; | |
+ | |
+ Bitmap_clear((UINT8 *) p_fs->vol_amap[i]->b_data, b); | |
+ | |
+ return (sector_write(sb, sector, p_fs->vol_amap[i], 0)); | |
+ | |
+#if EXFAT_CONFIG_DISCARD | |
+ if (opts->discard) { | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) | |
+ ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits)); | |
+#else | |
+ ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits), GFP_NOFS, 0); | |
+#endif | |
+ if (ret == -EOPNOTSUPP) { | |
+ printk(KERN_WARNING "discard not supported by device, disabling"); | |
+ opts->discard = 0; | |
+ } | |
+ } | |
+#endif /* EXFAT_CONFIG_DISCARD */ | |
+} /* end of clr_alloc_bitmap */ | |
+ | |
+UINT32 test_alloc_bitmap(struct super_block *sb, UINT32 clu) | |
+{ | |
+ INT32 i, map_i, map_b; | |
+ UINT32 clu_base, clu_free; | |
+ UINT8 k, clu_mask; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ clu_base = (clu & ~(0x7)) + 2; | |
+ clu_mask = (1 << (clu - clu_base + 2)) - 1; | |
+ | |
+ map_i = clu >> (p_bd->sector_size_bits + 3); | |
+ map_b = (clu >> 3) & p_bd->sector_size_mask; | |
+ | |
+ for (i = 2; i < p_fs->num_clusters; i += 8) { | |
+ k = *(((UINT8 *) p_fs->vol_amap[map_i]->b_data) + map_b); | |
+ if (clu_mask > 0) { | |
+ k |= clu_mask; | |
+ clu_mask = 0; | |
+ } | |
+ if (k < 0xFF) { | |
+ clu_free = clu_base + free_bit[k]; | |
+ if (clu_free < p_fs->num_clusters) | |
+ return(clu_free); | |
+ } | |
+ clu_base += 8; | |
+ | |
+ if (((++map_b) >= p_bd->sector_size) || (clu_base >= p_fs->num_clusters)) { | |
+ if ((++map_i) >= p_fs->map_sectors) { | |
+ clu_base = 2; | |
+ map_i = 0; | |
+ } | |
+ map_b = 0; | |
+ } | |
+ } | |
+ | |
+ return(CLUSTER_32(~0)); | |
+} /* end of test_alloc_bitmap */ | |
+ | |
+void sync_alloc_bitmap(struct super_block *sb) | |
+{ | |
+ INT32 i; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if (p_fs->vol_amap == NULL) | |
+ return; | |
+ | |
+ for (i = 0; i < p_fs->map_sectors; i++) { | |
+ sync_dirty_buffer(p_fs->vol_amap[i]); | |
+ } | |
+} /* end of sync_alloc_bitmap */ | |
+ | |
+/* | |
+ * Upcase table Management Functions | |
+ */ | |
+INT32 __load_upcase_table(struct super_block *sb, UINT32 sector, UINT32 num_sectors, UINT32 utbl_checksum) | |
+{ | |
+ INT32 i, ret = FFS_ERROR; | |
+ UINT32 j; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ struct buffer_head *tmp_bh = NULL; | |
+ | |
+ UINT8 skip = FALSE; | |
+ UINT32 index = 0; | |
+ UINT16 uni = 0; | |
+ UINT16 **upcase_table; | |
+ | |
+ UINT32 checksum = 0; | |
+ | |
+ upcase_table = p_fs->vol_utbl = (UINT16 **) MALLOC(UTBL_COL_COUNT * sizeof(UINT16 *)); | |
+ if(upcase_table == NULL) | |
+ return FFS_MEMORYERR; | |
+ MEMSET(upcase_table, 0, UTBL_COL_COUNT * sizeof(UINT16 *)); | |
+ | |
+ num_sectors += sector; | |
+ | |
+ while(sector < num_sectors) { | |
+ ret = sector_read(sb, sector, &tmp_bh, 1); | |
+ if (ret != FFS_SUCCESS) { | |
+ PRINTK("sector read (0x%X)fail\n", sector); | |
+ goto error; | |
+ } | |
+ sector++; | |
+ | |
+ for(i = 0; i < p_bd->sector_size && index <= 0xFFFF; i += 2) { | |
+ uni = GET16(((UINT8 *) tmp_bh->b_data)+i); | |
+ | |
+ checksum = ((checksum & 1) ? 0x80000000 : 0 ) + (checksum >> 1) + *(((UINT8 *) tmp_bh->b_data)+i); | |
+ checksum = ((checksum & 1) ? 0x80000000 : 0 ) + (checksum >> 1) + *(((UINT8 *) tmp_bh->b_data)+(i+1)); | |
+ | |
+ if(skip) { | |
+ PRINTK("skip from 0x%X ", index); | |
+ index += uni; | |
+ PRINTK("to 0x%X (amount of 0x%X)\n", index, uni); | |
+ skip = FALSE; | |
+ } else if(uni == index) | |
+ index++; | |
+ else if(uni == 0xFFFF) | |
+ skip = TRUE; | |
+ else { /* uni != index , uni != 0xFFFF */ | |
+ UINT16 col_index = get_col_index(index); | |
+ | |
+ if(upcase_table[col_index]== NULL) { | |
+ PRINTK("alloc = 0x%X\n", col_index); | |
+ upcase_table[col_index] = (UINT16 *) MALLOC(UTBL_ROW_COUNT * sizeof(UINT16)); | |
+ if(upcase_table[col_index] == NULL) { | |
+ ret = FFS_MEMORYERR; | |
+ goto error; | |
+ } | |
+ | |
+ for(j = 0 ; j < UTBL_ROW_COUNT ; j++) | |
+ upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; | |
+ } | |
+ | |
+ upcase_table[col_index][get_row_index(index)] = uni; | |
+ index++; | |
+ } | |
+ } | |
+ } | |
+ if(index >= 0xFFFF && utbl_checksum == checksum) { | |
+ if(tmp_bh) | |
+ brelse(tmp_bh); | |
+ return FFS_SUCCESS; | |
+ } | |
+ ret = FFS_ERROR; | |
+error: | |
+ if(tmp_bh) | |
+ brelse(tmp_bh); | |
+ free_upcase_table(sb); | |
+ return ret; | |
+} | |
+ | |
+INT32 __load_default_upcase_table(struct super_block *sb) | |
+{ | |
+ INT32 i, ret = FFS_ERROR; | |
+ UINT32 j; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ UINT8 skip = FALSE; | |
+ UINT32 index = 0; | |
+ UINT16 uni = 0; | |
+ UINT16 **upcase_table; | |
+ | |
+ upcase_table = p_fs->vol_utbl = (UINT16 **) MALLOC(UTBL_COL_COUNT * sizeof(UINT16 *)); | |
+ if(upcase_table == NULL) | |
+ return FFS_MEMORYERR; | |
+ MEMSET(upcase_table, 0, UTBL_COL_COUNT * sizeof(UINT16 *)); | |
+ | |
+ for(i = 0; index <= 0xFFFF && i < NUM_UPCASE*2; i += 2) { | |
+ uni = GET16(uni_upcase + i); | |
+ if(skip) { | |
+ PRINTK("skip from 0x%X ", index); | |
+ index += uni; | |
+ PRINTK("to 0x%X (amount of 0x%X)\n", index, uni); | |
+ skip = FALSE; | |
+ } else if(uni == index) | |
+ index++; | |
+ else if(uni == 0xFFFF) | |
+ skip = TRUE; | |
+ else { /* uni != index , uni != 0xFFFF */ | |
+ UINT16 col_index = get_col_index(index); | |
+ | |
+ if(upcase_table[col_index]== NULL) { | |
+ PRINTK("alloc = 0x%X\n", col_index); | |
+ upcase_table[col_index] = (UINT16 *) MALLOC(UTBL_ROW_COUNT * sizeof(UINT16)); | |
+ if(upcase_table[col_index] == NULL) { | |
+ ret = FFS_MEMORYERR; | |
+ goto error; | |
+ } | |
+ | |
+ for(j = 0 ; j < UTBL_ROW_COUNT ; j++) | |
+ upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; | |
+ } | |
+ | |
+ upcase_table[col_index][get_row_index(index)] = uni; | |
+ index ++; | |
+ } | |
+ } | |
+ | |
+ if(index >= 0xFFFF) | |
+ return FFS_SUCCESS; | |
+ | |
+error: | |
+ /* FATAL error: default upcase table has error */ | |
+ free_upcase_table(sb); | |
+ return ret; | |
+} | |
+ | |
+INT32 load_upcase_table(struct super_block *sb) | |
+{ | |
+ INT32 i; | |
+ UINT32 tbl_clu, tbl_size; | |
+ UINT32 type, sector, num_sectors; | |
+ CHAIN_T clu; | |
+ CASE_DENTRY_T *ep; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ clu.dir = p_fs->root_dir; | |
+ clu.flags = 0x01; | |
+ | |
+ if (p_fs->dev_ejected) | |
+ return FFS_MEDIAERR; | |
+ | |
+ while (clu.dir != CLUSTER_32(~0)) { | |
+ for (i = 0; i < p_fs->dentries_per_clu; i++) { | |
+ ep = (CASE_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); | |
+ if (!ep) | |
+ return FFS_MEDIAERR; | |
+ | |
+ type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); | |
+ | |
+ if (type == TYPE_UNUSED) | |
+ break; | |
+ if (type != TYPE_UPCASE) | |
+ continue; | |
+ | |
+ tbl_clu = GET32_A(ep->start_clu); | |
+ tbl_size = (UINT32) GET64_A(ep->size); | |
+ | |
+ sector = START_SECTOR(tbl_clu); | |
+ num_sectors = ((tbl_size-1) >> p_bd->sector_size_bits) + 1; | |
+ if(__load_upcase_table(sb, sector, num_sectors, GET32_A(ep->checksum)) != FFS_SUCCESS) | |
+ break; | |
+ else | |
+ return FFS_SUCCESS; | |
+ } | |
+ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) | |
+ return FFS_MEDIAERR; | |
+ } | |
+ /* load default upcase table */ | |
+ return __load_default_upcase_table(sb); | |
+} /* end of load_upcase_table */ | |
+ | |
+void free_upcase_table(struct super_block *sb) | |
+{ | |
+ UINT32 i; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ UINT16 **upcase_table; | |
+ | |
+ upcase_table = p_fs->vol_utbl; | |
+ for(i = 0 ; i < UTBL_COL_COUNT ; i ++) | |
+ FREE(upcase_table[i]); | |
+ | |
+ FREE(p_fs->vol_utbl); | |
+ | |
+ p_fs->vol_utbl = NULL; | |
+} /* end of free_upcase_table */ | |
+ | |
+/* | |
+ * Directory Entry Management Functions | |
+ */ | |
+ | |
+UINT32 fat_get_entry_type(DENTRY_T *p_entry) | |
+{ | |
+ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; | |
+ | |
+ if (*(ep->name) == 0x0) | |
+ return TYPE_UNUSED; | |
+ | |
+ else if (*(ep->name) == 0xE5) | |
+ return TYPE_DELETED; | |
+ | |
+ else if (ep->attr == ATTR_EXTEND) | |
+ return TYPE_EXTEND; | |
+ | |
+ else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_VOLUME) | |
+ return TYPE_VOLUME; | |
+ | |
+ else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_SUBDIR) | |
+ return TYPE_DIR; | |
+ | |
+ return TYPE_FILE; | |
+} /* end of fat_get_entry_type */ | |
+ | |
+UINT32 exfat_get_entry_type(DENTRY_T *p_entry) | |
+{ | |
+ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; | |
+ | |
+ if (ep->type == 0x0) { | |
+ return TYPE_UNUSED; | |
+ } else if (ep->type < 0x80) { | |
+ return TYPE_DELETED; | |
+ } else if (ep->type == 0x80) { | |
+ return TYPE_INVALID; | |
+ } else if (ep->type < 0xA0) { | |
+ if (ep->type == 0x81) { | |
+ return TYPE_BITMAP; | |
+ } else if (ep->type == 0x82) { | |
+ return TYPE_UPCASE; | |
+ } else if (ep->type == 0x83) { | |
+ return TYPE_VOLUME; | |
+ } else if (ep->type == 0x85) { | |
+ if (GET16_A(ep->attr) & ATTR_SUBDIR) | |
+ return TYPE_DIR; | |
+ else | |
+ return TYPE_FILE; | |
+ } | |
+ return TYPE_CRITICAL_PRI; | |
+ } else if (ep->type < 0xC0) { | |
+ if (ep->type == 0xA0) { | |
+ return TYPE_GUID; | |
+ } else if (ep->type == 0xA1) { | |
+ return TYPE_PADDING; | |
+ } else if (ep->type == 0xA2) { | |
+ return TYPE_ACLTAB; | |
+ } | |
+ return TYPE_BENIGN_PRI; | |
+ } else if (ep->type < 0xE0) { | |
+ if (ep->type == 0xC0) { | |
+ return TYPE_STREAM; | |
+ } else if (ep->type == 0xC1) { | |
+ return TYPE_EXTEND; | |
+ } else if (ep->type == 0xC2) { | |
+ return TYPE_ACL; | |
+ } | |
+ return TYPE_CRITICAL_SEC; | |
+ } | |
+ | |
+ return TYPE_BENIGN_SEC; | |
+} /* end of exfat_get_entry_type */ | |
+ | |
+void fat_set_entry_type(DENTRY_T *p_entry, UINT32 type) | |
+{ | |
+ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; | |
+ | |
+ if (type == TYPE_UNUSED) | |
+ *(ep->name) = 0x0; | |
+ | |
+ else if (type == TYPE_DELETED) | |
+ *(ep->name) = 0xE5; | |
+ | |
+ else if (type == TYPE_EXTEND) | |
+ ep->attr = ATTR_EXTEND; | |
+ | |
+ else if (type == TYPE_DIR) | |
+ ep->attr = ATTR_SUBDIR; | |
+ | |
+ else if (type == TYPE_FILE) | |
+ ep->attr = ATTR_ARCHIVE; | |
+ | |
+ else if (type == TYPE_SYMLINK) | |
+ ep->attr = ATTR_ARCHIVE | ATTR_SYMLINK; | |
+} /* end of fat_set_entry_type */ | |
+ | |
+void exfat_set_entry_type(DENTRY_T *p_entry, UINT32 type) | |
+{ | |
+ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; | |
+ | |
+ if (type == TYPE_UNUSED) { | |
+ ep->type = 0x0; | |
+ } else if (type == TYPE_DELETED) { | |
+ ep->type &= ~0x80; | |
+ } else if (type == TYPE_STREAM) { | |
+ ep->type = 0xC0; | |
+ } else if (type == TYPE_EXTEND) { | |
+ ep->type = 0xC1; | |
+ } else if (type == TYPE_BITMAP) { | |
+ ep->type = 0x81; | |
+ } else if (type == TYPE_UPCASE) { | |
+ ep->type = 0x82; | |
+ } else if (type == TYPE_VOLUME) { | |
+ ep->type = 0x83; | |
+ } else if (type == TYPE_DIR) { | |
+ ep->type = 0x85; | |
+ SET16_A(ep->attr, ATTR_SUBDIR); | |
+ } else if (type == TYPE_FILE) { | |
+ ep->type = 0x85; | |
+ SET16_A(ep->attr, ATTR_ARCHIVE); | |
+ } else if (type == TYPE_SYMLINK) { | |
+ ep->type = 0x85; | |
+ SET16_A(ep->attr, ATTR_ARCHIVE | ATTR_SYMLINK); | |
+ } | |
+} /* end of exfat_set_entry_type */ | |
+ | |
+UINT32 fat_get_entry_attr(DENTRY_T *p_entry) | |
+{ | |
+ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; | |
+ return((UINT32) ep->attr); | |
+} /* end of fat_get_entry_attr */ | |
+ | |
+UINT32 exfat_get_entry_attr(DENTRY_T *p_entry) | |
+{ | |
+ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; | |
+ return((UINT32) GET16_A(ep->attr)); | |
+} /* end of exfat_get_entry_attr */ | |
+ | |
+void fat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr) | |
+{ | |
+ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; | |
+ ep->attr = (UINT8) attr; | |
+} /* end of fat_set_entry_attr */ | |
+ | |
+void exfat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr) | |
+{ | |
+ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; | |
+ SET16_A(ep->attr, (UINT16) attr); | |
+} /* end of exfat_set_entry_attr */ | |
+ | |
+UINT8 fat_get_entry_flag(DENTRY_T *p_entry) | |
+{ | |
+ return 0x01; | |
+} /* end of fat_get_entry_flag */ | |
+ | |
+UINT8 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, UINT8 flags) | |
+{ | |
+} /* end of fat_set_entry_flag */ | |
+ | |
+void exfat_set_entry_flag(DENTRY_T *p_entry, UINT8 flags) | |
+{ | |
+ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; | |
+ ep->flags = flags; | |
+} /* end of exfat_set_entry_flag */ | |
+ | |
+UINT32 fat_get_entry_clu0(DENTRY_T *p_entry) | |
+{ | |
+ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; | |
+ return((GET32_A(ep->start_clu_hi) << 16) | GET16_A(ep->start_clu_lo)); | |
+} /* end of fat_get_entry_clu0 */ | |
+ | |
+UINT32 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, UINT32 start_clu) | |
+{ | |
+ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; | |
+ SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu)); | |
+ SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16)); | |
+} /* end of fat_set_entry_clu0 */ | |
+ | |
+void exfat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu) | |
+{ | |
+ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; | |
+ SET32_A(ep->start_clu, start_clu); | |
+} /* end of exfat_set_entry_clu0 */ | |
+ | |
+UINT64 fat_get_entry_size(DENTRY_T *p_entry) | |
+{ | |
+ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; | |
+ return((UINT64) GET32_A(ep->size)); | |
+} /* end of fat_get_entry_size */ | |
+ | |
+UINT64 exfat_get_entry_size(DENTRY_T *p_entry) | |
+{ | |
+ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; | |
+ return(GET64_A(ep->valid_size)); | |
+} /* end of exfat_get_entry_size */ | |
+ | |
+void fat_set_entry_size(DENTRY_T *p_entry, UINT64 size) | |
+{ | |
+ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; | |
+ SET32_A(ep->size, (UINT32) size); | |
+} /* end of fat_set_entry_size */ | |
+ | |
+void exfat_set_entry_size(DENTRY_T *p_entry, UINT64 size) | |
+{ | |
+ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; | |
+ SET64_A(ep->valid_size, size); | |
+ SET64_A(ep->size, size); | |
+} /* end of exfat_set_entry_size */ | |
+ | |
+void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) | |
+{ | |
+ UINT16 t = 0x00, d = 0x21; | |
+ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; | |
+ | |
+ switch (mode) { | |
+ case TM_CREATE: | |
+ t = GET16_A(ep->create_time); | |
+ d = GET16_A(ep->create_date); | |
+ break; | |
+ case TM_MODIFY: | |
+ t = GET16_A(ep->modify_time); | |
+ d = GET16_A(ep->modify_date); | |
+ break; | |
+ } | |
+ | |
+ tp->sec = (t & 0x001F) << 1; | |
+ tp->min = (t >> 5) & 0x003F; | |
+ tp->hour = (t >> 11); | |
+ tp->day = (d & 0x001F); | |
+ tp->mon = (d >> 5) & 0x000F; | |
+ tp->year = (d >> 9); | |
+} /* end of fat_get_entry_time */ | |
+ | |
+void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) | |
+{ | |
+ UINT16 t = 0x00, d = 0x21; | |
+ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; | |
+ | |
+ switch (mode) { | |
+ case TM_CREATE: | |
+ t = GET16_A(ep->create_time); | |
+ d = GET16_A(ep->create_date); | |
+ break; | |
+ case TM_MODIFY: | |
+ t = GET16_A(ep->modify_time); | |
+ d = GET16_A(ep->modify_date); | |
+ break; | |
+ case TM_ACCESS: | |
+ t = GET16_A(ep->access_time); | |
+ d = GET16_A(ep->access_date); | |
+ break; | |
+ } | |
+ | |
+ tp->sec = (t & 0x001F) << 1; | |
+ tp->min = (t >> 5) & 0x003F; | |
+ tp->hour = (t >> 11); | |
+ tp->day = (d & 0x001F); | |
+ tp->mon = (d >> 5) & 0x000F; | |
+ tp->year = (d >> 9); | |
+} /* end of exfat_get_entry_time */ | |
+ | |
+void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) | |
+{ | |
+ UINT16 t, d; | |
+ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; | |
+ | |
+ t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); | |
+ d = (tp->year << 9) | (tp->mon << 5) | tp->day; | |
+ | |
+ switch (mode) { | |
+ case TM_CREATE: | |
+ SET16_A(ep->create_time, t); | |
+ SET16_A(ep->create_date, d); | |
+ break; | |
+ case TM_MODIFY: | |
+ SET16_A(ep->modify_time, t); | |
+ SET16_A(ep->modify_date, d); | |
+ break; | |
+ } | |
+} /* end of fat_set_entry_time */ | |
+ | |
+void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) | |
+{ | |
+ UINT16 t, d; | |
+ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; | |
+ | |
+ t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); | |
+ d = (tp->year << 9) | (tp->mon << 5) | tp->day; | |
+ | |
+ switch (mode) { | |
+ case TM_CREATE: | |
+ SET16_A(ep->create_time, t); | |
+ SET16_A(ep->create_date, d); | |
+ break; | |
+ case TM_MODIFY: | |
+ SET16_A(ep->modify_time, t); | |
+ SET16_A(ep->modify_date, d); | |
+ break; | |
+ case TM_ACCESS: | |
+ SET16_A(ep->access_time, t); | |
+ SET16_A(ep->access_date, d); | |
+ break; | |
+ } | |
+} /* end of exfat_set_entry_time */ | |
+ | |
+INT32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, | |
+ UINT32 start_clu, UINT64 size) | |
+{ | |
+ UINT32 sector; | |
+ DOS_DENTRY_T *dos_ep; | |
+ | |
+ dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); | |
+ if (!dos_ep) | |
+ return FFS_MEDIAERR; | |
+ | |
+ init_dos_entry(dos_ep, type, start_clu); | |
+ buf_modify(sb, sector); | |
+ | |
+ return FFS_SUCCESS; | |
+} /* end of fat_init_dir_entry */ | |
+ | |
+INT32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, | |
+ UINT32 start_clu, UINT64 size) | |
+{ | |
+ UINT32 sector; | |
+ UINT8 flags; | |
+ FILE_DENTRY_T *file_ep; | |
+ STRM_DENTRY_T *strm_ep; | |
+ | |
+ flags = (type == TYPE_FILE) ? 0x01 : 0x03; | |
+ | |
+ /* 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 */ | |
+ | |
+INT32 fat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, | |
+ UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) | |
+{ | |
+ INT32 i; | |
+ UINT32 sector; | |
+ UINT8 chksum; | |
+ UINT16 *uniname = p_uniname->name; | |
+ DOS_DENTRY_T *dos_ep; | |
+ EXT_DENTRY_T *ext_ep; | |
+ | |
+ dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); | |
+ if (!dos_ep) | |
+ return FFS_MEDIAERR; | |
+ | |
+ dos_ep->lcase = p_dosname->name_case; | |
+ MEMCPY(dos_ep->name, p_dosname->name, DOS_NAME_LENGTH); | |
+ buf_modify(sb, sector); | |
+ | |
+ if ((--num_entries) > 0) { | |
+ chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); | |
+ | |
+ for (i = 1; i < num_entries; i++) { | |
+ ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); | |
+ if (!ext_ep) | |
+ return FFS_MEDIAERR; | |
+ | |
+ init_ext_entry(ext_ep, i, chksum, uniname); | |
+ buf_modify(sb, sector); | |
+ uniname += 13; | |
+ } | |
+ | |
+ ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); | |
+ if (!ext_ep) | |
+ return FFS_MEDIAERR; | |
+ | |
+ init_ext_entry(ext_ep, i+0x40, chksum, uniname); | |
+ buf_modify(sb, sector); | |
+ } | |
+ | |
+ return FFS_SUCCESS; | |
+} /* end of fat_init_ext_entry */ | |
+ | |
+INT32 exfat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, | |
+ UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) | |
+{ | |
+ INT32 i; | |
+ UINT32 sector; | |
+ UINT16 *uniname = p_uniname->name; | |
+ FILE_DENTRY_T *file_ep; | |
+ STRM_DENTRY_T *strm_ep; | |
+ NAME_DENTRY_T *name_ep; | |
+ | |
+ file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); | |
+ if (!file_ep) | |
+ return FFS_MEDIAERR; | |
+ | |
+ file_ep->num_ext = (UINT8)(num_entries - 1); | |
+ buf_modify(sb, sector); | |
+ | |
+ strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, §or); | |
+ if (!strm_ep) | |
+ return FFS_MEDIAERR; | |
+ | |
+ strm_ep->name_len = p_uniname->name_len; | |
+ SET16_A(strm_ep->name_hash, p_uniname->name_hash); | |
+ buf_modify(sb, sector); | |
+ | |
+ for (i = 2; i < num_entries; i++) { | |
+ name_ep = (NAME_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+i, §or); | |
+ if (!name_ep) | |
+ return FFS_MEDIAERR; | |
+ | |
+ init_name_entry(name_ep, uniname); | |
+ buf_modify(sb, sector); | |
+ uniname += 15; | |
+ } | |
+ | |
+ update_dir_checksum(sb, p_dir, entry); | |
+ | |
+ return FFS_SUCCESS; | |
+} /* end of exfat_init_ext_entry */ | |
+ | |
+void init_dos_entry(DOS_DENTRY_T *ep, UINT32 type, UINT32 start_clu) | |
+{ | |
+ TIMESTAMP_T tm, *tp; | |
+ | |
+ fat_set_entry_type((DENTRY_T *) ep, type); | |
+ SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu)); | |
+ SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16)); | |
+ SET32_A(ep->size, 0); | |
+ | |
+ tp = tm_current(&tm); | |
+ fat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); | |
+ fat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); | |
+ SET16_A(ep->access_date, 0); | |
+ ep->create_time_ms = 0; | |
+} /* end of init_dos_entry */ | |
+ | |
+void init_ext_entry(EXT_DENTRY_T *ep, INT32 order, UINT8 chksum, UINT16 *uniname) | |
+{ | |
+ INT32 i; | |
+ UINT8 end = FALSE; | |
+ | |
+ fat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); | |
+ ep->order = (UINT8) order; | |
+ ep->sysid = 0; | |
+ ep->checksum = chksum; | |
+ SET16_A(ep->start_clu, 0); | |
+ | |
+ for (i = 0; i < 10; i += 2) { | |
+ if (!end) { | |
+ SET16(ep->unicode_0_4+i, *uniname); | |
+ if (*uniname == 0x0) | |
+ end = TRUE; | |
+ else | |
+ uniname++; | |
+ } else { | |
+ SET16(ep->unicode_0_4+i, 0xFFFF); | |
+ } | |
+ } | |
+ | |
+ for (i = 0; i < 12; i += 2) { | |
+ if (!end) { | |
+ SET16_A(ep->unicode_5_10+i, *uniname); | |
+ if (*uniname == 0x0) | |
+ end = TRUE; | |
+ else | |
+ uniname++; | |
+ } else { | |
+ SET16_A(ep->unicode_5_10+i, 0xFFFF); | |
+ } | |
+ } | |
+ | |
+ for (i = 0; i < 4; i += 2) { | |
+ if (!end) { | |
+ SET16_A(ep->unicode_11_12+i, *uniname); | |
+ if (*uniname == 0x0) | |
+ end = TRUE; | |
+ else | |
+ uniname++; | |
+ } else { | |
+ SET16_A(ep->unicode_11_12+i, 0xFFFF); | |
+ } | |
+ } | |
+} /* end of init_ext_entry */ | |
+ | |
+void init_file_entry(FILE_DENTRY_T *ep, UINT32 type) | |
+{ | |
+ TIMESTAMP_T tm, *tp; | |
+ | |
+ exfat_set_entry_type((DENTRY_T *) ep, type); | |
+ | |
+ tp = tm_current(&tm); | |
+ exfat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); | |
+ exfat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); | |
+ exfat_set_entry_time((DENTRY_T *) ep, tp, TM_ACCESS); | |
+ ep->create_time_ms = 0; | |
+ ep->modify_time_ms = 0; | |
+ ep->access_time_ms = 0; | |
+} /* end of init_file_entry */ | |
+ | |
+void init_strm_entry(STRM_DENTRY_T *ep, UINT8 flags, UINT32 start_clu, UINT64 size) | |
+{ | |
+ exfat_set_entry_type((DENTRY_T *) ep, TYPE_STREAM); | |
+ ep->flags = flags; | |
+ SET32_A(ep->start_clu, start_clu); | |
+ SET64_A(ep->valid_size, size); | |
+ SET64_A(ep->size, size); | |
+} /* end of init_strm_entry */ | |
+ | |
+void init_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname) | |
+{ | |
+ INT32 i; | |
+ | |
+ exfat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); | |
+ ep->flags = 0x0; | |
+ | |
+ for (i = 0; i < 30; i++, i++) { | |
+ SET16_A(ep->unicode_0_14+i, *uniname); | |
+ if (*uniname == 0x0) | |
+ break; | |
+ uniname++; | |
+ } | |
+} /* end of init_name_entry */ | |
+ | |
+void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries) | |
+{ | |
+ INT32 i; | |
+ UINT32 sector; | |
+ DENTRY_T *ep; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ for (i = num_entries-1; i >= order; i--) { | |
+ ep = get_entry_in_dir(sb, p_dir, entry-i, §or); | |
+ if (!ep) | |
+ return; | |
+ | |
+ p_fs->fs_func->set_entry_type(ep, TYPE_DELETED); | |
+ buf_modify(sb, sector); | |
+ } | |
+} /* end of fat_delete_dir_entry */ | |
+ | |
+void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries) | |
+{ | |
+ INT32 i; | |
+ UINT32 sector; | |
+ DENTRY_T *ep; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ for (i = order; i < num_entries; i++) { | |
+ ep = get_entry_in_dir(sb, p_dir, entry+i, §or); | |
+ if (!ep) | |
+ return; | |
+ | |
+ p_fs->fs_func->set_entry_type(ep, TYPE_DELETED); | |
+ buf_modify(sb, sector); | |
+ } | |
+} /* end of exfat_delete_dir_entry */ | |
+ | |
+void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, INT32 entry) | |
+{ | |
+ INT32 i, num_entries; | |
+ UINT32 sector; | |
+ UINT16 chksum; | |
+ FILE_DENTRY_T *file_ep; | |
+ DENTRY_T *ep; | |
+ | |
+ file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); | |
+ if (!file_ep) | |
+ return; | |
+ | |
+ buf_lock(sb, sector); | |
+ | |
+ num_entries = (INT32) file_ep->num_ext + 1; | |
+ chksum = calc_checksum_2byte((void *) file_ep, DENTRY_SIZE, 0, CS_DIR_ENTRY); | |
+ | |
+ for (i = 1; i < num_entries; i++) { | |
+ ep = get_entry_in_dir(sb, p_dir, entry+i, NULL); | |
+ if (!ep) { | |
+ buf_unlock(sb, sector); | |
+ return; | |
+ } | |
+ | |
+ chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, CS_DEFAULT); | |
+ } | |
+ | |
+ SET16_A(file_ep->checksum, chksum); | |
+ buf_modify(sb, sector); | |
+ buf_unlock(sb, sector); | |
+} /* end of update_dir_checksum */ | |
+ | |
+void update_dir_checksum_with_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es) | |
+{ | |
+ DENTRY_T *ep; | |
+ UINT16 chksum = 0; | |
+ INT32 chksum_type = CS_DIR_ENTRY, i; | |
+ | |
+ ep = (DENTRY_T *)&(es->__buf); | |
+ for (i=0; i < es->num_entries; i++) { | |
+ PRINTK ("update_dir_checksum_with_entry_set ep %p\n", ep); | |
+ chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, chksum_type); | |
+ ep++; | |
+ chksum_type = CS_DEFAULT; | |
+ } | |
+ | |
+ ep = (DENTRY_T *)&(es->__buf); | |
+ SET16_A(((FILE_DENTRY_T *)ep)->checksum, chksum); | |
+ write_whole_entry_set(sb, es); | |
+} | |
+ | |
+static INT32 _walk_fat_chain (struct super_block *sb, CHAIN_T *p_dir, INT32 byte_offset, UINT32 *clu) | |
+{ | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ INT32 clu_offset; | |
+ UINT32 cur_clu; | |
+ | |
+ clu_offset = byte_offset >> p_fs->cluster_size_bits; | |
+ cur_clu = p_dir->dir; | |
+ | |
+ if (p_dir->flags == 0x03) { | |
+ cur_clu += clu_offset; | |
+ } else { | |
+ while (clu_offset > 0) { | |
+ if (FAT_read(sb, cur_clu, &cur_clu) == -1) | |
+ return FFS_MEDIAERR; | |
+ clu_offset--; | |
+ } | |
+ } | |
+ | |
+ if (clu) | |
+ *clu = cur_clu; | |
+ return FFS_SUCCESS; | |
+} | |
+INT32 find_location(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector, INT32 *offset) | |
+{ | |
+ INT32 off, ret; | |
+ UINT32 clu=0; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ off = entry << DENTRY_SIZE_BITS; | |
+ | |
+ if (p_dir->dir == CLUSTER_32(0)) { /* 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, UINT32 sector, INT32 offset) | |
+{ | |
+ UINT8 *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, INT32 entry, UINT32 *sector) | |
+{ | |
+ INT32 off; | |
+ UINT32 sec; | |
+ UINT8 *buf; | |
+ | |
+ if (find_location(sb, p_dir, entry, &sec, &off) != FFS_SUCCESS) | |
+ return NULL; | |
+ | |
+ buf = buf_getblk(sb, sec); | |
+ | |
+ if (buf == NULL) | |
+ return NULL; | |
+ | |
+ if (sector != NULL) | |
+ *sector = sec; | |
+ return((DENTRY_T *)(buf + off)); | |
+} /* 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, INT32 entry, UINT32 type, DENTRY_T **file_ep) | |
+{ | |
+ INT32 off, ret, byte_offset; | |
+ UINT32 clu=0; | |
+ UINT32 sec, entry_type; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ ENTRY_SET_CACHE_T *es = NULL; | |
+ DENTRY_T *ep, *pos; | |
+ UINT8 *buf; | |
+ UINT8 num_entries; | |
+ INT32 mode = ES_MODE_STARTED; | |
+ | |
+ PRINTK("get_entry_set_in_dir entered\n"); | |
+ PRINTK("p_dir dir %u flags %x size %d\n", p_dir->dir, p_dir->flags, p_dir->size); | |
+ | |
+ byte_offset = entry << DENTRY_SIZE_BITS; | |
+ ret =_walk_fat_chain(sb, p_dir, byte_offset, &clu); | |
+ if (ret != FFS_SUCCESS) | |
+ return NULL; | |
+ | |
+ | |
+ byte_offset &= p_fs->cluster_size - 1; /* 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; | |
+ | |
+ PRINTK("trying to malloc %x bytes for %d entries\n", offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T), num_entries); | |
+ es = MALLOC(offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T)); | |
+ if (es == NULL) | |
+ goto err_out; | |
+ | |
+ es->num_entries = num_entries; | |
+ es->sector = sec; | |
+ es->offset = off; | |
+ es->alloc_flag = p_dir->flags; | |
+ | |
+ pos = (DENTRY_T *) &(es->__buf); | |
+ | |
+ while(num_entries) { | |
+ /* 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; | |
+ } | |
+ | |
+ COPY_DENTRY(pos, ep); | |
+ | |
+ 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); | |
+ | |
+ PRINTK("es sec %u offset %d flags %d, num_entries %u buf ptr %p\n", | |
+ es->sector, es->offset, es->alloc_flag, es->num_entries, &(es->__buf)); | |
+ PRINTK("get_entry_set_in_dir exited %p\n", es); | |
+ return es; | |
+err_out: | |
+ PRINTK("get_entry_set_in_dir exited NULL (es %p)\n", es); | |
+ FREE(es); | |
+ return NULL; | |
+} | |
+ | |
+void release_entry_set (ENTRY_SET_CACHE_T *es) | |
+{ | |
+ PRINTK("release_entry_set %p\n", es); | |
+ FREE(es); | |
+} | |
+ | |
+ | |
+static INT32 __write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, UINT32 sec, INT32 off, UINT32 count) | |
+{ | |
+ INT32 num_entries, buf_off = (off - es->offset); | |
+ UINT32 remaining_byte_in_sector, copy_entries; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ UINT32 clu; | |
+ UINT8 *buf, *esbuf = (UINT8 *)&(es->__buf); | |
+ | |
+ PRINTK("__write_partial_entries_in_entry_set entered\n"); | |
+ PRINTK("es %p sec %u off %d count %d\n", es, sec, off, count); | |
+ num_entries = count; | |
+ | |
+ while(num_entries) { | |
+ /* 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; | |
+ PRINTK("es->buf %p buf_off %u\n", esbuf, buf_off); | |
+ PRINTK("copying %d entries from %p to sector %u\n", copy_entries, (esbuf + buf_off), sec); | |
+ MEMCPY(buf + off, esbuf + buf_off, copy_entries << DENTRY_SIZE_BITS); | |
+ buf_modify(sb, sec); | |
+ num_entries -= copy_entries; | |
+ | |
+ if (num_entries) { | |
+ /* 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; | |
+ } | |
+ } | |
+ | |
+ PRINTK("__write_partial_entries_in_entry_set exited successfully\n"); | |
+ return FFS_SUCCESS; | |
+err_out: | |
+ PRINTK("__write_partial_entries_in_entry_set failed\n"); | |
+ return FFS_ERROR; | |
+} | |
+ | |
+/* write back all entries in entry set */ | |
+INT32 write_whole_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es) | |
+{ | |
+ return (__write_partial_entries_in_entry_set(sb, es, es->sector,es->offset, es->num_entries)); | |
+} | |
+ | |
+/* write back some entries in entry set */ | |
+INT32 write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, UINT32 count) | |
+{ | |
+ INT32 ret, byte_offset, off; | |
+ UINT32 clu=0, sec; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ CHAIN_T dir; | |
+ | |
+ /* 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 += ((INT32 *)ep - (INT32 *)&(es->__buf)) + es->offset; | |
+ | |
+ ret =_walk_fat_chain(sb, &dir, byte_offset, &clu); | |
+ if (ret != FFS_SUCCESS) | |
+ return ret; | |
+ byte_offset &= p_fs->cluster_size - 1; /* 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 */ | |
+INT32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 num_entries) | |
+{ | |
+ INT32 i, dentry, num_empty = 0; | |
+ INT32 dentries_per_clu; | |
+ UINT32 type; | |
+ CHAIN_T clu; | |
+ DENTRY_T *ep; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if (p_dir->dir == CLUSTER_32(0)) /* 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 */ | |
+ | |
+INT32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, INT32 num_entries) | |
+{ | |
+ INT32 ret, dentry; | |
+ UINT32 last_clu, sector; | |
+ UINT64 size = 0; | |
+ CHAIN_T clu; | |
+ DENTRY_T *ep = NULL; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); | |
+ | |
+ if (p_dir->dir == CLUSTER_32(0)) /* 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) | |
+ FAT_write(sb, last_clu, clu.dir); | |
+ | |
+ if (p_fs->hint_uentry.entry == -1) { | |
+ p_fs->hint_uentry.dir = p_dir->dir; | |
+ p_fs->hint_uentry.entry = p_dir->size << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); | |
+ | |
+ p_fs->hint_uentry.clu.dir = clu.dir; | |
+ p_fs->hint_uentry.clu.size = 0; | |
+ p_fs->hint_uentry.clu.flags = clu.flags; | |
+ } | |
+ p_fs->hint_uentry.clu.size++; | |
+ p_dir->size++; | |
+ | |
+ /* (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 */ | |
+INT32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type) | |
+{ | |
+ INT32 i, dentry = 0, len; | |
+ INT32 order = 0, is_feasible_entry = TRUE, has_ext_entry = FALSE; | |
+ INT32 dentries_per_clu; | |
+ UINT32 entry_type; | |
+ UINT16 entry_uniname[14], *uniname = NULL, unichar; | |
+ CHAIN_T clu; | |
+ DENTRY_T *ep; | |
+ DOS_DENTRY_T *dos_ep; | |
+ EXT_DENTRY_T *ext_ep; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if (p_dir->dir == p_fs->root_dir) { | |
+ if ((!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_CUR_DIR_NAME)) || | |
+ (!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_PAR_DIR_NAME))) | |
+ return -1; // 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 (!nls_dosname_cmp(sb, p_dosname->name, dos_ep->name)) | |
+ return(dentry); | |
+ } | |
+ is_feasible_entry = TRUE; | |
+ has_ext_entry = FALSE; | |
+ } else if (entry_type == TYPE_EXTEND) { | |
+ if (is_feasible_entry) { | |
+ ext_ep = (EXT_DENTRY_T *) ep; | |
+ if (ext_ep->order > 0x40) { | |
+ order = (INT32)(ext_ep->order - 0x40); | |
+ uniname = p_uniname->name + 13 * (order-1); | |
+ } else { | |
+ order = (INT32) ext_ep->order; | |
+ uniname -= 13; | |
+ } | |
+ | |
+ len = extract_uni_name_from_ext_entry(ext_ep, entry_uniname, order); | |
+ | |
+ unichar = *(uniname+len); | |
+ *(uniname+len) = 0x0; | |
+ | |
+ if (nls_uniname_cmp(sb, uniname, entry_uniname)) { | |
+ is_feasible_entry = FALSE; | |
+ } | |
+ | |
+ *(uniname+len) = unichar; | |
+ } | |
+ has_ext_entry = TRUE; | |
+ } else if (entry_type == TYPE_UNUSED) { | |
+ return -2; | |
+ } else { | |
+ is_feasible_entry = TRUE; | |
+ has_ext_entry = FALSE; | |
+ } | |
+ } | |
+ | |
+ if (p_dir->dir == CLUSTER_32(0)) | |
+ break; /* 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 */ | |
+INT32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type) | |
+{ | |
+ INT32 i, dentry = 0, num_ext_entries = 0, len; | |
+ INT32 order = 0, is_feasible_entry = FALSE; | |
+ INT32 dentries_per_clu, num_empty = 0; | |
+ UINT32 entry_type; | |
+ UINT16 entry_uniname[16], *uniname = NULL, unichar; | |
+ CHAIN_T clu; | |
+ DENTRY_T *ep; | |
+ FILE_DENTRY_T *file_ep; | |
+ STRM_DENTRY_T *strm_ep; | |
+ NAME_DENTRY_T *name_ep; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if (p_dir->dir == p_fs->root_dir) { | |
+ if ((!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_CUR_DIR_NAME)) || | |
+ (!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_PAR_DIR_NAME))) | |
+ return -1; // 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; | |
+ | |
+ for (i = 0; i < dentries_per_clu; i++, dentry++) { | |
+ ep = get_entry_in_dir(sb, &clu, i, NULL); | |
+ if (!ep) | |
+ return -2; | |
+ | |
+ entry_type = p_fs->fs_func->get_entry_type(ep); | |
+ | |
+ if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) { | |
+ is_feasible_entry = FALSE; | |
+ | |
+ if (p_fs->hint_uentry.entry == -1) { | |
+ num_empty++; | |
+ | |
+ if (num_empty == 1) { | |
+ p_fs->hint_uentry.clu.dir = clu.dir; | |
+ p_fs->hint_uentry.clu.size = clu.size; | |
+ p_fs->hint_uentry.clu.flags = clu.flags; | |
+ } | |
+ if ((num_empty >= num_entries) || (entry_type == TYPE_UNUSED)) { | |
+ p_fs->hint_uentry.entry = dentry - (num_empty-1); | |
+ } | |
+ } | |
+ | |
+ if (entry_type == TYPE_UNUSED) { | |
+ return -2; | |
+ } | |
+ } else { | |
+ num_empty = 0; | |
+ | |
+ if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) { | |
+ if ((type == TYPE_ALL) || (type == entry_type)) { | |
+ file_ep = (FILE_DENTRY_T *) ep; | |
+ num_ext_entries = file_ep->num_ext; | |
+ is_feasible_entry = TRUE; | |
+ } else { | |
+ is_feasible_entry = FALSE; | |
+ } | |
+ } else if (entry_type == TYPE_STREAM) { | |
+ if (is_feasible_entry) { | |
+ strm_ep = (STRM_DENTRY_T *) ep; | |
+ if (p_uniname->name_len == strm_ep->name_len) { | |
+ order = 1; | |
+ } else { | |
+ is_feasible_entry = FALSE; | |
+ } | |
+ } | |
+ } else if (entry_type == TYPE_EXTEND) { | |
+ if (is_feasible_entry) { | |
+ name_ep = (NAME_DENTRY_T *) ep; | |
+ | |
+ if ((++order) == 2) | |
+ uniname = p_uniname->name; | |
+ else | |
+ uniname += 15; | |
+ | |
+ len = extract_uni_name_from_name_entry(name_ep, entry_uniname, order); | |
+ | |
+ unichar = *(uniname+len); | |
+ *(uniname+len) = 0x0; | |
+ | |
+ if (nls_uniname_cmp(sb, uniname, entry_uniname)) { | |
+ is_feasible_entry = FALSE; | |
+ } else if (order == num_ext_entries) { | |
+ p_fs->hint_uentry.dir = CLUSTER_32(~0); | |
+ p_fs->hint_uentry.entry = -1; | |
+ return(dentry - (num_ext_entries)); | |
+ } | |
+ | |
+ *(uniname+len) = unichar; | |
+ } | |
+ } else { | |
+ is_feasible_entry = FALSE; | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (p_dir->dir == CLUSTER_32(0)) | |
+ break; /* 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 */ | |
+INT32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry) | |
+{ | |
+ INT32 count = 0; | |
+ UINT8 chksum; | |
+ DOS_DENTRY_T *dos_ep = (DOS_DENTRY_T *) p_entry; | |
+ EXT_DENTRY_T *ext_ep; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); | |
+ | |
+ for (entry--; entry >= 0; entry--) { | |
+ ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL); | |
+ if (!ext_ep) | |
+ return -1; | |
+ | |
+ if ((p_fs->fs_func->get_entry_type((DENTRY_T *) ext_ep) == TYPE_EXTEND) && | |
+ (ext_ep->checksum == chksum)) { | |
+ count++; | |
+ if (ext_ep->order > 0x40) | |
+ return(count); | |
+ } else { | |
+ return(count); | |
+ } | |
+ } | |
+ | |
+ return(count); | |
+} /* end of fat_count_ext_entries */ | |
+ | |
+/* returns -1 on error */ | |
+INT32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry) | |
+{ | |
+ INT32 i, count = 0; | |
+ UINT32 type; | |
+ FILE_DENTRY_T *file_ep = (FILE_DENTRY_T *) p_entry; | |
+ DENTRY_T *ext_ep; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ for (i = 0, entry++; i < file_ep->num_ext; i++, entry++) { | |
+ ext_ep = get_entry_in_dir(sb, p_dir, entry, NULL); | |
+ if (!ext_ep) | |
+ return -1; | |
+ | |
+ type = p_fs->fs_func->get_entry_type(ext_ep); | |
+ if ((type == TYPE_EXTEND) || (type == TYPE_STREAM)) { | |
+ count++; | |
+ } else { | |
+ return(count); | |
+ } | |
+ } | |
+ | |
+ return(count); | |
+} /* end of exfat_count_ext_entries */ | |
+ | |
+/* returns -1 on error */ | |
+INT32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, UINT32 type) | |
+{ | |
+ INT32 i, count = 0; | |
+ INT32 dentries_per_clu; | |
+ UINT32 entry_type; | |
+ CHAIN_T clu; | |
+ DENTRY_T *ep; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if (p_dir->dir == CLUSTER_32(0)) /* 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) | |
+{ | |
+ INT32 i, count = 0; | |
+ INT32 dentries_per_clu; | |
+ UINT32 type; | |
+ CHAIN_T clu; | |
+ DENTRY_T *ep; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if (p_dir->dir == CLUSTER_32(0)) /* 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) */ | |
+INT32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 *entries, DOS_NAME_T *p_dosname) | |
+{ | |
+ INT32 ret, num_entries, lossy = FALSE; | |
+ INT8 **r; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ num_entries = p_fs->fs_func->calc_num_entries(p_uniname); | |
+ if (num_entries == 0) | |
+ return FFS_INVALIDPATH; | |
+ | |
+ if (p_fs->vol_type != EXFAT) { | |
+ nls_uniname_to_dosname(sb, p_dosname, p_uniname, &lossy); | |
+ | |
+ if (lossy) { | |
+ ret = fat_generate_dos_name(sb, p_dir, p_dosname); | |
+ if (ret) | |
+ return ret; | |
+ } else { | |
+ for (r = reserved_names; *r; r++) { | |
+ if (!STRNCMP((void *) p_dosname->name, *r, 8)) | |
+ return FFS_INVALIDPATH; | |
+ } | |
+ | |
+ if (p_dosname->name_case != 0xFF) | |
+ num_entries = 1; | |
+ } | |
+ | |
+ if (num_entries > 1) | |
+ p_dosname->name_case = 0x0; | |
+ } | |
+ | |
+ *entries = num_entries; | |
+ | |
+ return FFS_SUCCESS; | |
+} /* 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, UINT8 mode) | |
+{ | |
+ DOS_NAME_T dos_name; | |
+ | |
+ if (mode == 0x0) | |
+ dos_name.name_case = 0x0; | |
+ else | |
+ dos_name.name_case = ep->lcase; | |
+ | |
+ MEMCPY(dos_name.name, ep->name, DOS_NAME_LENGTH); | |
+ nls_dosname_to_uniname(sb, p_uniname, &dos_name); | |
+} /* end of get_uni_name_from_dos_entry */ | |
+ | |
+void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname) | |
+{ | |
+ INT32 i; | |
+ EXT_DENTRY_T *ep; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ for (entry--, i = 1; entry >= 0; entry--, i++) { | |
+ ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL); | |
+ if (!ep) | |
+ return; | |
+ | |
+ if (p_fs->fs_func->get_entry_type((DENTRY_T *) ep) == TYPE_EXTEND) { | |
+ extract_uni_name_from_ext_entry(ep, uniname, i); | |
+ if (ep->order > 0x40) | |
+ return; | |
+ } else { | |
+ return; | |
+ } | |
+ | |
+ uniname += 13; | |
+ } | |
+} /* 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, INT32 entry, UINT16 *uniname) | |
+{ | |
+ INT32 i; | |
+ DENTRY_T *ep; | |
+ ENTRY_SET_CACHE_T *es; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ es = get_entry_set_in_dir(sb, p_dir, entry, ES_ALL_ENTRIES, &ep); | |
+ if (es == NULL || es->num_entries < 3) { | |
+ if(es) { | |
+ release_entry_set(es); | |
+ } | |
+ return; | |
+ } | |
+ | |
+ ep += 2; | |
+ | |
+ /* | |
+ * 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 { | |
+ /* end of name entry */ | |
+ goto out; | |
+ } | |
+ uniname += 15; | |
+ } | |
+ | |
+out: | |
+ release_entry_set(es); | |
+} /* end of exfat_get_uni_name_from_ext_entry */ | |
+ | |
+INT32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, UINT16 *uniname, INT32 order) | |
+{ | |
+ INT32 i, len = 0; | |
+ | |
+ for (i = 0; i < 10; i += 2) { | |
+ *uniname = GET16(ep->unicode_0_4+i); | |
+ if (*uniname == 0x0) | |
+ return(len); | |
+ uniname++; | |
+ len++; | |
+ } | |
+ | |
+ if (order < 20) { | |
+ for (i = 0; i < 12; i += 2) { | |
+ *uniname = GET16_A(ep->unicode_5_10+i); | |
+ if (*uniname == 0x0) | |
+ return(len); | |
+ uniname++; | |
+ len++; | |
+ } | |
+ } else { | |
+ for (i = 0; i < 8; i += 2) { | |
+ *uniname = GET16_A(ep->unicode_5_10+i); | |
+ if (*uniname == 0x0) | |
+ return(len); | |
+ uniname++; | |
+ len++; | |
+ } | |
+ *uniname = 0x0; /* 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 */ | |
+ | |
+INT32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname, INT32 order) | |
+{ | |
+ INT32 i, len = 0; | |
+ | |
+ for (i = 0; i < 30; i += 2) { | |
+ *uniname = GET16_A(ep->unicode_0_14+i); | |
+ if (*uniname == 0x0) | |
+ return(len); | |
+ uniname++; | |
+ len++; | |
+ } | |
+ | |
+ *uniname = 0x0; | |
+ return(len); | |
+ | |
+} /* end of extract_uni_name_from_name_entry */ | |
+ | |
+INT32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname) | |
+{ | |
+ INT32 i, j, count = 0, count_begin = FALSE; | |
+ INT32 dentries_per_clu; | |
+ UINT32 type; | |
+ UINT8 bmap[128/* 1 ~ 1023 */]; | |
+ CHAIN_T clu; | |
+ DOS_DENTRY_T *ep; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ Bitmap_clear_all(bmap, 128); | |
+ Bitmap_set(bmap, 0); | |
+ | |
+ if (p_dir->dir == CLUSTER_32(0)) /* 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)) | |
+ 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 (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(UINT8 *dosname, INT32 count) | |
+{ | |
+ INT32 i, j, length; | |
+ INT8 str_count[6]; | |
+ | |
+ str_count[0] = '~'; | |
+ str_count[1] = '\0'; | |
+ my_itoa(&(str_count[1]), count); | |
+ length = STRLEN(str_count); | |
+ | |
+ i = j = 0; | |
+ while (j <= (8 - length)) { | |
+ i = j; | |
+ if (dosname[j] == ' ') | |
+ break; | |
+ if (dosname[j] & 0x80) | |
+ j += 2; | |
+ else | |
+ j++; | |
+ } | |
+ | |
+ for (j = 0; j < length; i++, j++) | |
+ dosname[i] = (UINT8) str_count[j]; | |
+ | |
+ if (i == 7) | |
+ dosname[7] = ' '; | |
+ | |
+} /* end of attach_count_to_dos_name */ | |
+ | |
+INT32 fat_calc_num_entries(UNI_NAME_T *p_uniname) | |
+{ | |
+ INT32 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 */ | |
+ | |
+INT32 exfat_calc_num_entries(UNI_NAME_T *p_uniname) | |
+{ | |
+ INT32 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 */ | |
+ | |
+UINT8 calc_checksum_1byte(void *data, INT32 len, UINT8 chksum) | |
+{ | |
+ INT32 i; | |
+ UINT8 *c = (UINT8 *) data; | |
+ | |
+ for (i = 0; i < len; i++, c++) | |
+ chksum = (((chksum & 1) << 7) | ((chksum & 0xFE) >> 1)) + *c; | |
+ | |
+ return(chksum); | |
+} /* end of calc_checksum_1byte */ | |
+ | |
+UINT16 calc_checksum_2byte(void *data, INT32 len, UINT16 chksum, INT32 type) | |
+{ | |
+ INT32 i; | |
+ UINT8 *c = (UINT8 *) data; | |
+ | |
+ switch (type) { | |
+ case CS_DIR_ENTRY: | |
+ for (i = 0; i < len; i++, c++) { | |
+ if ((i == 2) || (i == 3)) | |
+ continue; | |
+ chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (UINT16) *c; | |
+ } | |
+ break; | |
+ default | |
+ : | |
+ for (i = 0; i < len; i++, c++) { | |
+ chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (UINT16) *c; | |
+ } | |
+ } | |
+ | |
+ return(chksum); | |
+} /* end of calc_checksum_2byte */ | |
+ | |
+UINT32 calc_checksum_4byte(void *data, INT32 len, UINT32 chksum, INT32 type) | |
+{ | |
+ INT32 i; | |
+ UINT8 *c = (UINT8 *) data; | |
+ | |
+ switch (type) { | |
+ case CS_PBR_SECTOR: | |
+ for (i = 0; i < len; i++, c++) { | |
+ if ((i == 106) || (i == 107) || (i == 112)) | |
+ continue; | |
+ chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (UINT32) *c; | |
+ } | |
+ break; | |
+ default | |
+ : | |
+ for (i = 0; i < len; i++, c++) { | |
+ chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (UINT32) *c; | |
+ } | |
+ } | |
+ | |
+ return(chksum); | |
+} /* end of calc_checksum_4byte */ | |
+ | |
+/* | |
+ * Name Resolution Functions | |
+ */ | |
+ | |
+/* return values of resolve_path() | |
+ > 0 : return the length of the path | |
+ < 0 : return error */ | |
+INT32 resolve_path(struct inode *inode, UINT8 *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname) | |
+{ | |
+ INT32 lossy = FALSE; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); | |
+ | |
+ if (STRLEN(path) >= (MAX_NAME_LENGTH * MAX_CHARSET_SIZE)) | |
+ return(FFS_INVALIDPATH); | |
+ | |
+ STRCPY(name_buf, path); | |
+ | |
+ nls_cstring_to_uniname(sb, p_uniname, name_buf, &lossy); | |
+ if (lossy) | |
+ return(FFS_INVALIDPATH); | |
+ | |
+ fid->size = i_size_read(inode); | |
+ | |
+ p_dir->dir = fid->start_clu; | |
+ p_dir->size = (INT32)(fid->size >> p_fs->cluster_size_bits); | |
+ p_dir->flags = fid->flags; | |
+ | |
+ return(FFS_SUCCESS); | |
+} | |
+ | |
+/* | |
+ * 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, | |
+}; | |
+ | |
+ | |
+INT32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) | |
+{ | |
+ INT32 num_reserved, num_root_sectors; | |
+ BPB16_T *p_bpb = (BPB16_T *) p_pbr->bpb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ if (p_bpb->num_fats == 0) | |
+ return FFS_FORMATERR; | |
+ | |
+ num_root_sectors = GET16(p_bpb->num_root_entries) << DENTRY_SIZE_BITS; | |
+ num_root_sectors = ((num_root_sectors-1) >> p_bd->sector_size_bits) + 1; | |
+ | |
+ p_fs->sectors_per_clu = p_bpb->sectors_per_clu; | |
+ p_fs->sectors_per_clu_bits = my_log2(p_bpb->sectors_per_clu); | |
+ p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; | |
+ p_fs->cluster_size = 1 << p_fs->cluster_size_bits; | |
+ | |
+ p_fs->num_FAT_sectors = GET16(p_bpb->num_fat_sectors); | |
+ | |
+ p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved); | |
+ if (p_bpb->num_fats == 1) | |
+ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; | |
+ else | |
+ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; | |
+ | |
+ p_fs->root_start_sector = p_fs->FAT2_start_sector + p_fs->num_FAT_sectors; | |
+ p_fs->data_start_sector = p_fs->root_start_sector + num_root_sectors; | |
+ | |
+ p_fs->num_sectors = GET16(p_bpb->num_sectors); | |
+ if (p_fs->num_sectors == 0) | |
+ p_fs->num_sectors = GET32(p_bpb->num_huge_sectors); | |
+ | |
+ num_reserved = p_fs->data_start_sector - p_fs->PBR_sector; | |
+ p_fs->num_clusters = ((p_fs->num_sectors - num_reserved) >> p_fs->sectors_per_clu_bits) + 2; | |
+ /* 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 = (UINT32) ~0; | |
+ | |
+ p_fs->fs_func = &fat_fs_func; | |
+ | |
+ return FFS_SUCCESS; | |
+} /* end of fat16_mount */ | |
+ | |
+INT32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) | |
+{ | |
+ INT32 num_reserved; | |
+ BPB32_T *p_bpb = (BPB32_T *) p_pbr->bpb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ if (p_bpb->num_fats == 0) | |
+ return FFS_FORMATERR; | |
+ | |
+ p_fs->sectors_per_clu = p_bpb->sectors_per_clu; | |
+ p_fs->sectors_per_clu_bits = my_log2(p_bpb->sectors_per_clu); | |
+ p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; | |
+ p_fs->cluster_size = 1 << p_fs->cluster_size_bits; | |
+ | |
+ p_fs->num_FAT_sectors = GET32(p_bpb->num_fat32_sectors); | |
+ | |
+ p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved); | |
+ if (p_bpb->num_fats == 1) | |
+ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; | |
+ else | |
+ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; | |
+ | |
+ p_fs->root_start_sector = p_fs->FAT2_start_sector + p_fs->num_FAT_sectors; | |
+ p_fs->data_start_sector = p_fs->root_start_sector; | |
+ | |
+ p_fs->num_sectors = GET32(p_bpb->num_huge_sectors); | |
+ num_reserved = p_fs->data_start_sector - p_fs->PBR_sector; | |
+ | |
+ p_fs->num_clusters = ((p_fs->num_sectors-num_reserved) >> p_fs->sectors_per_clu_bits) + 2; | |
+ /* 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 = (UINT32) ~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, | |
+}; | |
+ | |
+INT32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) | |
+{ | |
+ BPBEX_T *p_bpb = (BPBEX_T *) p_pbr->bpb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); | |
+ | |
+ if (p_bpb->num_fats == 0) | |
+ return FFS_FORMATERR; | |
+ | |
+ p_fs->sectors_per_clu = 1 << p_bpb->sectors_per_clu_bits; | |
+ p_fs->sectors_per_clu_bits = p_bpb->sectors_per_clu_bits; | |
+ p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; | |
+ p_fs->cluster_size = 1 << p_fs->cluster_size_bits; | |
+ | |
+ p_fs->num_FAT_sectors = GET32(p_bpb->fat_length); | |
+ | |
+ p_fs->FAT1_start_sector = p_fs->PBR_sector + GET32(p_bpb->fat_offset); | |
+ if (p_bpb->num_fats == 1) | |
+ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; | |
+ else | |
+ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; | |
+ | |
+ p_fs->root_start_sector = p_fs->PBR_sector + GET32(p_bpb->clu_offset); | |
+ p_fs->data_start_sector = p_fs->root_start_sector; | |
+ | |
+ p_fs->num_sectors = GET64(p_bpb->vol_length); | |
+ p_fs->num_clusters = GET32(p_bpb->clu_count) + 2; | |
+ /* 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 = (UINT32) GET16(p_bpb->vol_flags); | |
+ p_fs->clu_srch_ptr = 2; | |
+ p_fs->used_clusters = (UINT32) ~0; | |
+ | |
+ p_fs->fs_func = &exfat_fs_func; | |
+ | |
+ return FFS_SUCCESS; | |
+} /* end of exfat_mount */ | |
+ | |
+INT32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) | |
+{ | |
+ INT32 ret, dentry, num_entries; | |
+ UINT64 size; | |
+ CHAIN_T clu; | |
+ DOS_NAME_T dos_name, dot_name; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ /* 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 < 1) | |
+ return FFS_FULL; | |
+ | |
+ ret = clear_cluster(sb, clu.dir); | |
+ if (ret != FFS_SUCCESS) | |
+ return ret; | |
+ | |
+ if (p_fs->vol_type == EXFAT) { | |
+ size = p_fs->cluster_size; | |
+ } else { | |
+ size = 0; | |
+ | |
+ /* 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 */ | |
+ | |
+INT32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, UINT8 mode, FILE_ID_T *fid) | |
+{ | |
+ INT32 ret, dentry, num_entries; | |
+ DOS_NAME_T dos_name; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ /* 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, INT32 entry) | |
+{ | |
+ INT32 num_entries; | |
+ UINT32 sector; | |
+ DENTRY_T *ep; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ ep = get_entry_in_dir(sb, p_dir, entry, §or); | |
+ if (!ep) | |
+ return; | |
+ | |
+ buf_lock(sb, sector); | |
+ | |
+ /* 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 */ | |
+ | |
+INT32 rename_file(struct inode *inode, CHAIN_T *p_dir, INT32 oldentry, UNI_NAME_T *p_uniname, FILE_ID_T *fid) | |
+{ | |
+ INT32 ret, newentry = -1, num_old_entries, num_new_entries; | |
+ UINT32 sector_old, sector_new; | |
+ DOS_NAME_T dos_name; | |
+ DENTRY_T *epold, *epnew; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ epold = get_entry_in_dir(sb, p_dir, oldentry, §or_old); | |
+ if (!epold) | |
+ return FFS_MEDIAERR; | |
+ | |
+ buf_lock(sb, sector_old); | |
+ | |
+ /* 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 */ | |
+ | |
+INT32 move_file(struct inode *inode, CHAIN_T *p_olddir, INT32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) | |
+{ | |
+ INT32 ret, newentry, num_new_entries, num_old_entries; | |
+ UINT32 sector_mov, sector_new; | |
+ CHAIN_T clu; | |
+ DOS_NAME_T dos_name; | |
+ DENTRY_T *epmov, *epnew; | |
+ struct super_block *sb = inode->i_sb; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ epmov = get_entry_in_dir(sb, p_olddir, oldentry, §or_mov); | |
+ if (!epmov) | |
+ return FFS_MEDIAERR; | |
+ | |
+ /* 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 | |
+ */ | |
+ | |
+INT32 sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 read) | |
+{ | |
+ INT32 ret = FFS_MEDIAERR; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if ((sec >= (p_fs->PBR_sector+p_fs->num_sectors)) && (p_fs->num_sectors > 0)) { | |
+ PRINT("[EXFAT] sector_read: out of range error! (sec = %d)\n", sec); | |
+ fs_error(sb); | |
+ return ret; | |
+ } | |
+ | |
+ if (!p_fs->dev_ejected) { | |
+ ret = bdev_read(sb, sec, bh, 1, read); | |
+ if (ret != FFS_SUCCESS) | |
+ p_fs->dev_ejected = TRUE; | |
+ } | |
+ | |
+ return ret; | |
+} /* end of sector_read */ | |
+ | |
+INT32 sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 sync) | |
+{ | |
+ INT32 ret = FFS_MEDIAERR; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if (sec >= (p_fs->PBR_sector+p_fs->num_sectors) && (p_fs->num_sectors > 0)) { | |
+ PRINT("[EXFAT] sector_write: out of range error! (sec = %d)\n", sec); | |
+ fs_error(sb); | |
+ return ret; | |
+ } | |
+ if (bh == NULL) { | |
+ PRINT("[EXFAT] sector_write: bh is NULL!\n"); | |
+ fs_error(sb); | |
+ return ret; | |
+ } | |
+ | |
+ if (!p_fs->dev_ejected) { | |
+ ret = bdev_write(sb, sec, bh, 1, sync); | |
+ if (ret != FFS_SUCCESS) | |
+ p_fs->dev_ejected = TRUE; | |
+ } | |
+ | |
+ return ret; | |
+} /* end of sector_write */ | |
+ | |
+INT32 multi_sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 num_secs, INT32 read) | |
+{ | |
+ INT32 ret = FFS_MEDIAERR; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if (((sec+num_secs) > (p_fs->PBR_sector+p_fs->num_sectors)) && (p_fs->num_sectors > 0)) { | |
+ PRINT("[EXFAT] multi_sector_read: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs); | |
+ fs_error(sb); | |
+ return ret; | |
+ } | |
+ | |
+ if (!p_fs->dev_ejected) { | |
+ ret = bdev_read(sb, sec, bh, num_secs, read); | |
+ if (ret != FFS_SUCCESS) | |
+ p_fs->dev_ejected = TRUE; | |
+ } | |
+ | |
+ return ret; | |
+} /* end of multi_sector_read */ | |
+ | |
+INT32 multi_sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 num_secs, INT32 sync) | |
+{ | |
+ INT32 ret = FFS_MEDIAERR; | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if ((sec+num_secs) > (p_fs->PBR_sector+p_fs->num_sectors) && (p_fs->num_sectors > 0)) { | |
+ PRINT("[EXFAT] multi_sector_write: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs); | |
+ fs_error(sb); | |
+ return ret; | |
+ } | |
+ if (bh == NULL) { | |
+ PRINT("[EXFAT] multi_sector_write: bh is NULL!\n"); | |
+ fs_error(sb); | |
+ return ret; | |
+ } | |
+ | |
+ if (!p_fs->dev_ejected) { | |
+ ret = bdev_write(sb, sec, bh, num_secs, sync); | |
+ if (ret != FFS_SUCCESS) | |
+ p_fs->dev_ejected = TRUE; | |
+ } | |
+ | |
+ return ret; | |
+} /* end of multi_sector_write */ | |
+ | |
+/* end of exfat_core.c */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_data.c exfat_kernel-2.6.32/fs/exfat/exfat_data.c | |
--- kernel-2.6.32/fs/exfat/exfat_data.c 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_data.c 2013-08-31 23:02:03.000000000 +1100 | |
@@ -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_global.h" | |
+#include "exfat_data.h" | |
+#include "exfat_oal.h" | |
+ | |
+#include "exfat_blkdev.h" | |
+#include "exfat_cache.h" | |
+#include "exfat_nls.h" | |
+#include "exfat_super.h" | |
+#include "exfat.h" | |
+ | |
+/*======================================================================*/ | |
+/* */ | |
+/* GLOBAL VARIABLE DEFINITIONS */ | |
+/* */ | |
+/*======================================================================*/ | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* File Manager */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+/* file system volume table */ | |
+FS_STRUCT_T fs_struct[MAX_DRIVE]; | |
+ | |
+#if 0 | |
+/*----------------------------------------------------------------------*/ | |
+/* Buffer Manager */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+/* FAT cache */ | |
+DECLARE_MUTEX(f_sem); | |
+BUF_CACHE_T FAT_cache_array[FAT_CACHE_SIZE]; | |
+BUF_CACHE_T FAT_cache_lru_list; | |
+BUF_CACHE_T FAT_cache_hash_list[FAT_CACHE_HASH_SIZE]; | |
+ | |
+/* buf cache */ | |
+DECLARE_MUTEX(b_sem); | |
+BUF_CACHE_T buf_cache_array[BUF_CACHE_SIZE]; | |
+BUF_CACHE_T buf_cache_lru_list; | |
+BUF_CACHE_T buf_cache_hash_list[BUF_CACHE_HASH_SIZE]; | |
+#endif | |
+ | |
+/* end of exfat_data.c */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_data.h exfat_kernel-2.6.32/fs/exfat/exfat_data.h | |
--- kernel-2.6.32/fs/exfat/exfat_data.h 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_data.h 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,83 @@ | |
+/* | |
+ * 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" | |
+#include "exfat_global.h" | |
+ | |
+#ifdef __cplusplus | |
+extern "C" { | |
+#endif /* __cplusplus */ | |
+ | |
+ /*======================================================================*/ | |
+ /* */ | |
+ /* FFS CONFIGURATIONS */ | |
+ /* (CHANGE THIS PART IF REQUIRED) */ | |
+ /* */ | |
+ /*======================================================================*/ | |
+ | |
+ /* max number of block devices */ | |
+#define MAX_DEVICE 2 | |
+ | |
+ /* max number of volumes on all block devices */ | |
+#define MAX_DRIVE 2 | |
+ | |
+ /* max number of open files */ | |
+#define MAX_OPEN 20 | |
+ | |
+ /* max number of root directory entries in FAT12/16 */ | |
+ /* (should be an exponential value of 2) */ | |
+#define MAX_DENTRY 512 | |
+ | |
+ /* cache size (in number of sectors) */ | |
+ /* (should be an exponential value of 2) */ | |
+#define FAT_CACHE_SIZE 128 | |
+#define FAT_CACHE_HASH_SIZE 64 | |
+#define BUF_CACHE_SIZE 256 | |
+#define BUF_CACHE_HASH_SIZE 64 | |
+ | |
+#ifndef CONFIG_EXFAT_DEFAULT_CODEPAGE | |
+#define CONFIG_EXFAT_DEFAULT_CODEPAGE 437 | |
+#define CONFIG_EXFAT_DEFAULT_IOCHARSET "utf8" | |
+#endif | |
+ | |
+#ifdef __cplusplus | |
+} | |
+#endif /* __cplusplus */ | |
+ | |
+#endif /* _EXFAT_DATA_H */ | |
+ | |
+/* end of exfat_data.h */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_global.c exfat_kernel-2.6.32/fs/exfat/exfat_global.c | |
--- kernel-2.6.32/fs/exfat/exfat_global.c 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_global.c 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,168 @@ | |
+/* | |
+ * 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_global.h" | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Global Variable Definitions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+/*======================================================================*/ | |
+/* */ | |
+/* LIBRARY FUNCTION DEFINITIONS -- WELL-KNOWN FUNCTIONS */ | |
+/* */ | |
+/*======================================================================*/ | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* String Manipulation Functions */ | |
+/* (defined if no system memory functions are available) */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+INT32 __wstrchr(UINT16 *str, UINT16 wchar) | |
+{ | |
+ while (*str) { | |
+ if (*(str++) == wchar) return(1); | |
+ } | |
+ return(0); | |
+} | |
+ | |
+INT32 __wstrlen(UINT16 *str) | |
+{ | |
+ INT32 length = 0; | |
+ | |
+ while (*(str++)) length++; | |
+ return(length); | |
+} | |
+ | |
+/*======================================================================*/ | |
+/* */ | |
+/* LIBRARY FUNCTION DEFINITIONS -- OTHER UTILITY FUNCTIONS */ | |
+/* */ | |
+/*======================================================================*/ | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Bitmap Manipulation Functions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+#define BITMAP_LOC(v) ((v) >> 3) | |
+#define BITMAP_SHIFT(v) ((v) & 0x07) | |
+ | |
+void Bitmap_set_all(UINT8 *bitmap, INT32 mapsize) | |
+{ | |
+ MEMSET(bitmap, 0xFF, mapsize); | |
+} /* end of Bitmap_set_all */ | |
+ | |
+void Bitmap_clear_all(UINT8 *bitmap, INT32 mapsize) | |
+{ | |
+ MEMSET(bitmap, 0x0, mapsize); | |
+} /* end of Bitmap_clear_all */ | |
+ | |
+INT32 Bitmap_test(UINT8 *bitmap, INT32 i) | |
+{ | |
+ UINT8 data; | |
+ | |
+ data = bitmap[BITMAP_LOC(i)]; | |
+ if ((data >> BITMAP_SHIFT(i)) & 0x01) return(1); | |
+ return(0); | |
+} /* end of Bitmap_test */ | |
+ | |
+void Bitmap_set(UINT8 *bitmap, INT32 i) | |
+{ | |
+ bitmap[BITMAP_LOC(i)] |= (0x01 << BITMAP_SHIFT(i)); | |
+} /* end of Bitmap_set */ | |
+ | |
+void Bitmap_clear(UINT8 *bitmap, INT32 i) | |
+{ | |
+ bitmap[BITMAP_LOC(i)] &= ~(0x01 << BITMAP_SHIFT(i)); | |
+} /* end of Bitmap_clear */ | |
+ | |
+void Bitmap_nbits_set(UINT8 *bitmap, INT32 offset, INT32 nbits) | |
+{ | |
+ INT32 i; | |
+ | |
+ for (i = 0; i < nbits; i++) { | |
+ Bitmap_set(bitmap, offset+i); | |
+ } | |
+} /* end of Bitmap_nbits_set */ | |
+ | |
+void Bitmap_nbits_clear(UINT8 *bitmap, INT32 offset, INT32 nbits) | |
+{ | |
+ INT32 i; | |
+ | |
+ for (i = 0; i < nbits; i++) { | |
+ Bitmap_clear(bitmap, offset+i); | |
+ } | |
+} /* end of Bitmap_nbits_clear */ | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Miscellaneous Library Functions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+/* integer to ascii conversion */ | |
+void my_itoa(INT8 *buf, INT32 v) | |
+{ | |
+ INT32 mod[10]; | |
+ INT32 i; | |
+ | |
+ for (i = 0; i < 10; i++) { | |
+ mod[i] = (v % 10); | |
+ v = v / 10; | |
+ if (v == 0) break; | |
+ } | |
+ | |
+ if (i == 10) | |
+ i--; | |
+ | |
+ for (; i >= 0; i--) { | |
+ *buf = (UINT8) ('0' + mod[i]); | |
+ buf++; | |
+ } | |
+ *buf = '\0'; | |
+} /* end of my_itoa */ | |
+ | |
+/* value to base 2 log conversion */ | |
+INT32 my_log2(UINT32 v) | |
+{ | |
+ UINT32 bits = 0; | |
+ | |
+ while (v > 1) { | |
+ if (v & 0x01) return(-1); | |
+ v >>= 1; | |
+ bits++; | |
+ } | |
+ return(bits); | |
+} /* end of my_log2 */ | |
+ | |
+/* end of exfat_global.c */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_global.h exfat_kernel-2.6.32/fs/exfat/exfat_global.h | |
--- kernel-2.6.32/fs/exfat/exfat_global.h 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_global.h 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,214 @@ | |
+/* | |
+ * 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_GLOBAL_H | |
+#define _EXFAT_GLOBAL_H | |
+ | |
+#include <linux/kernel.h> | |
+#include <linux/mm.h> | |
+#include <linux/slab.h> | |
+#include <linux/string.h> | |
+#include <linux/fs.h> | |
+ | |
+#include "exfat_config.h" | |
+ | |
+#ifdef __cplusplus | |
+extern "C" { | |
+#endif /* __cplusplus */ | |
+ | |
+ /*======================================================================*/ | |
+ /* */ | |
+ /* CONSTANT & MACRO DEFINITIONS */ | |
+ /* */ | |
+ /*======================================================================*/ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Well-Known Constants (DO NOT CHANGE THIS PART !!) */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+#ifndef TRUE | |
+#define TRUE 1 | |
+#endif | |
+#ifndef FALSE | |
+#define FALSE 0 | |
+#endif | |
+#ifndef OK | |
+#define OK 0 | |
+#endif | |
+#ifndef FAIL | |
+#define FAIL 1 | |
+#endif | |
+#ifndef NULL | |
+#define NULL 0 | |
+#endif | |
+ | |
+ /* Min/Max macro */ | |
+#define MIN(a, b) (((a) < (b)) ? (a) : (b)) | |
+#define MAX(a, b) (((a) > (b)) ? (a) : (b)) | |
+ | |
+ /*======================================================================*/ | |
+ /* */ | |
+ /* TYPE DEFINITIONS */ | |
+ /* (CHANGE THIS PART IF REQUIRED) */ | |
+ /* */ | |
+ /*======================================================================*/ | |
+ | |
+ /* type definitions for primitive types; | |
+ these should be re-defined to meet its size for each OS platform; | |
+ these should be used instead of primitive types for portability. */ | |
+ | |
+ typedef char INT8; // 1 byte signed integer | |
+ typedef short INT16; // 2 byte signed integer | |
+ typedef int INT32; // 4 byte signed integer | |
+ typedef long long INT64; // 8 byte signed integer | |
+ | |
+ typedef unsigned char UINT8; // 1 byte unsigned integer | |
+ typedef unsigned short UINT16; // 2 byte unsigned integer | |
+ typedef unsigned int UINT32; // 4 byte unsigned integer | |
+ typedef unsigned long long UINT64; // 8 byte ussigned integer | |
+ | |
+ typedef unsigned char BOOL; | |
+ | |
+ | |
+ /*======================================================================*/ | |
+ /* */ | |
+ /* LIBRARY FUNCTION DECLARATIONS -- WELL-KNOWN FUNCTIONS */ | |
+ /* (CHANGE THIS PART IF REQUIRED) */ | |
+ /* */ | |
+ /*======================================================================*/ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Memory Manipulation Macros & Functions */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+#ifdef MALLOC | |
+#undef MALLOC | |
+#endif | |
+#ifdef FREE | |
+#undef FREE | |
+#endif | |
+#ifdef MEMSET | |
+#undef MEMSET | |
+#endif | |
+#ifdef MEMCPY | |
+#undef MEMCPY | |
+#endif | |
+#ifdef MEMCMP | |
+#undef MEMCMP | |
+#endif | |
+ | |
+#define MALLOC(size) kmalloc(size, GFP_KERNEL) | |
+#define FREE(mem) if (mem) kfree(mem) | |
+#define MEMSET(mem, value, size) memset(mem, value, size) | |
+#define MEMCPY(dest, src, size) memcpy(dest, src, size) | |
+#define MEMCMP(mem1, mem2, size) memcmp(mem1, mem2, size) | |
+#define COPY_DENTRY(dest, src) memcpy(dest, src, sizeof(DENTRY_T)) | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* String Manipulation Macros & Functions */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+#define STRCPY(dest, src) strcpy(dest, src) | |
+#define STRNCPY(dest, src, n) strncpy(dest, src, n) | |
+#define STRCAT(str1, str2) strcat(str1, str2) | |
+#define STRCMP(str1, str2) strcmp(str1, str2) | |
+#define STRNCMP(str1, str2, n) strncmp(str1, str2, n) | |
+#define STRLEN(str) strlen(str) | |
+ | |
+ INT32 __wstrchr(UINT16 *str, UINT16 wchar); | |
+ INT32 __wstrlen(UINT16 *str); | |
+ | |
+#define WSTRCHR(str, wchar) __wstrchr(str, wchar) | |
+#define WSTRLEN(str) __wstrlen(str) | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Debugging Macros & Functions */ | |
+ /* EXFAT_CONFIG_DEBUG_MSG is configured in exfat_config.h */ | |
+ /*----------------------------------------------------------------------*/ | |
+#if EXFAT_CONFIG_DEBUG_MSG | |
+#define PRINTK(...) \ | |
+ do { \ | |
+ printk("[EXFAT] " __VA_ARGS__); \ | |
+ } while(0) | |
+#else | |
+#define PRINTK(...) | |
+#endif | |
+ | |
+ /*======================================================================*/ | |
+ /* */ | |
+ /* LIBRARY FUNCTION DECLARATIONS -- OTHER UTILITY FUNCTIONS */ | |
+ /* (DO NOT CHANGE THIS PART !!) */ | |
+ /* */ | |
+ /*======================================================================*/ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Bitmap Manipulation Functions */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ void Bitmap_set_all(UINT8 *bitmap, INT32 mapsize); | |
+ void Bitmap_clear_all(UINT8 *bitmap, INT32 mapsize); | |
+ INT32 Bitmap_test(UINT8 *bitmap, INT32 i); | |
+ void Bitmap_set(UINT8 *bitmap, INT32 i); | |
+ void Bitmap_clear(UINT8 *bitmpa, INT32 i); | |
+ void Bitmap_nbits_set(UINT8 *bitmap, INT32 offset, INT32 nbits); | |
+ void Bitmap_nbits_clear(UINT8 *bitmap, INT32 offset, INT32 nbits); | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Miscellaneous Library Functions */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ void my_itoa(INT8 *buf, INT32 v); | |
+ INT32 my_log2(UINT32 v); | |
+ | |
+ /*======================================================================*/ | |
+ /* */ | |
+ /* DEFINITIONS FOR DEBUGGING */ | |
+ /* (CHANGE THIS PART IF REQUIRED) */ | |
+ /* */ | |
+ /*======================================================================*/ | |
+ | |
+ /* debug message ouput macro */ | |
+#ifdef PRINT | |
+#undef PRINT | |
+#endif | |
+ | |
+#define PRINT printk | |
+ | |
+#ifdef __cplusplus | |
+} | |
+#endif /* __cplusplus */ | |
+ | |
+#endif /* _EXFAT_GLOBAL_H */ | |
+ | |
+/* end of exfat_global.h */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat.h exfat_kernel-2.6.32/fs/exfat/exfat.h | |
--- kernel-2.6.32/fs/exfat/exfat.h 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat.h 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,675 @@ | |
+/* | |
+ * 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.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_global.h" | |
+#include "exfat_data.h" | |
+#include "exfat_oal.h" | |
+ | |
+#include "exfat_blkdev.h" | |
+#include "exfat_cache.h" | |
+#include "exfat_nls.h" | |
+#include "exfat_api.h" | |
+#include "exfat_cache.h" | |
+ | |
+#ifdef __cplusplus | |
+extern "C" { | |
+#endif /* __cplusplus */ | |
+ | |
+#if EXFAT_CONFIG_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 /* EXFAT_CONFIG_KERNEL_DEBUG */ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Constant & Macro Definitions */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+#define MAX_VOLUME 4 // max num of volumes per device | |
+ | |
+#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) ((UINT16)(x)) | |
+#define CLUSTER_32(x) ((UINT32)(x)) | |
+ | |
+#define START_SECTOR(x) \ | |
+ ( (((x)-2) << p_fs->sectors_per_clu_bits) + p_fs->data_start_sector ) | |
+ | |
+#define IS_LAST_SECTOR_IN_CLUSTER(sec) \ | |
+ ( (((sec) - p_fs->data_start_sector + 1) & ((1 << p_fs->sectors_per_clu_bits) -1)) == 0) | |
+ | |
+#define GET_CLUSTER_FROM_SECTOR(sec) \ | |
+ ((((sec) - p_fs->data_start_sector) >> p_fs->sectors_per_clu_bits) +2) | |
+ | |
+#define GET16(p_src) \ | |
+ ( ((UINT16)(p_src)[0]) | (((UINT16)(p_src)[1]) << 8) ) | |
+#define GET32(p_src) \ | |
+ ( ((UINT32)(p_src)[0]) | (((UINT32)(p_src)[1]) << 8) | \ | |
+ (((UINT32)(p_src)[2]) << 16) | (((UINT32)(p_src)[3]) << 24) ) | |
+#define GET64(p_src) \ | |
+ ( ((UINT64)(p_src)[0]) | (((UINT64)(p_src)[1]) << 8) | \ | |
+ (((UINT64)(p_src)[2]) << 16) | (((UINT64)(p_src)[3]) << 24) | \ | |
+ (((UINT64)(p_src)[4]) << 32) | (((UINT64)(p_src)[5]) << 40) | \ | |
+ (((UINT64)(p_src)[6]) << 48) | (((UINT64)(p_src)[7]) << 56) ) | |
+ | |
+ | |
+#define SET16(p_dst,src) \ | |
+ do { \ | |
+ (p_dst)[0]=(UINT8)(src); \ | |
+ (p_dst)[1]=(UINT8)(((UINT16)(src)) >> 8); \ | |
+ } while (0) | |
+#define SET32(p_dst,src) \ | |
+ do { \ | |
+ (p_dst)[0]=(UINT8)(src); \ | |
+ (p_dst)[1]=(UINT8)(((UINT32)(src)) >> 8); \ | |
+ (p_dst)[2]=(UINT8)(((UINT32)(src)) >> 16); \ | |
+ (p_dst)[3]=(UINT8)(((UINT32)(src)) >> 24); \ | |
+ } while (0) | |
+#define SET64(p_dst,src) \ | |
+ do { \ | |
+ (p_dst)[0]=(UINT8)(src); \ | |
+ (p_dst)[1]=(UINT8)(((UINT64)(src)) >> 8); \ | |
+ (p_dst)[2]=(UINT8)(((UINT64)(src)) >> 16); \ | |
+ (p_dst)[3]=(UINT8)(((UINT64)(src)) >> 24); \ | |
+ (p_dst)[4]=(UINT8)(((UINT64)(src)) >> 32); \ | |
+ (p_dst)[5]=(UINT8)(((UINT64)(src)) >> 40); \ | |
+ (p_dst)[6]=(UINT8)(((UINT64)(src)) >> 48); \ | |
+ (p_dst)[7]=(UINT8)(((UINT64)(src)) >> 56); \ | |
+ } while (0) | |
+ | |
+#ifdef __LITTLE_ENDIAN | |
+#define GET16_A(p_src) (*((UINT16 *)(p_src))) | |
+#define GET32_A(p_src) (*((UINT32 *)(p_src))) | |
+#define GET64_A(p_src) (*((UINT64 *)(p_src))) | |
+#define SET16_A(p_dst,src) *((UINT16 *)(p_dst)) = (UINT16)(src) | |
+#define SET32_A(p_dst,src) *((UINT32 *)(p_dst)) = (UINT32)(src) | |
+#define SET64_A(p_dst,src) *((UINT64 *)(p_dst)) = (UINT64)(src) | |
+#else | |
+#define GET16_A(p_src) GET16(p_src) | |
+#define GET32_A(p_src) GET32(p_src) | |
+#define GET64_A(p_src) GET64(p_src) | |
+#define SET16_A(p_dst,src) SET16(p_dst, src) | |
+#define SET32_A(p_dst,src) SET32(p_dst, src) | |
+#define SET64_A(p_dst,src) SET64(p_dst, src) | |
+#endif | |
+ | |
+ /* Upcase tabel mecro */ | |
+#define HIGH_INDEX_BIT (8) | |
+#define HIGH_INDEX_MASK (0xFF00) | |
+#define LOW_INDEX_BIT (16-HIGH_INDEX_BIT) | |
+#define UTBL_ROW_COUNT (1<<LOW_INDEX_BIT) | |
+#define UTBL_COL_COUNT (1<<HIGH_INDEX_BIT) | |
+ | |
+ static inline UINT16 get_col_index(UINT16 i) | |
+ { | |
+ return i >> LOW_INDEX_BIT; | |
+ } | |
+ static inline UINT16 get_row_index(UINT16 i) | |
+ { | |
+ return i & ~HIGH_INDEX_MASK; | |
+ } | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Type Definitions */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ /* MS_DOS FAT partition boot record (512 bytes) */ | |
+ typedef struct { | |
+ UINT8 jmp_boot[3]; | |
+ UINT8 oem_name[8]; | |
+ UINT8 bpb[109]; | |
+ UINT8 boot_code[390]; | |
+ UINT8 signature[2]; | |
+ } PBR_SECTOR_T; | |
+ | |
+ /* MS-DOS FAT12/16 BIOS parameter block (51 bytes) */ | |
+ typedef struct { | |
+ UINT8 sector_size[2]; | |
+ UINT8 sectors_per_clu; | |
+ UINT8 num_reserved[2]; | |
+ UINT8 num_fats; | |
+ UINT8 num_root_entries[2]; | |
+ UINT8 num_sectors[2]; | |
+ UINT8 media_type; | |
+ UINT8 num_fat_sectors[2]; | |
+ UINT8 sectors_in_track[2]; | |
+ UINT8 num_heads[2]; | |
+ UINT8 num_hid_sectors[4]; | |
+ UINT8 num_huge_sectors[4]; | |
+ | |
+ UINT8 phy_drv_no; | |
+ UINT8 reserved; | |
+ UINT8 ext_signature; | |
+ UINT8 vol_serial[4]; | |
+ UINT8 vol_label[11]; | |
+ UINT8 vol_type[8]; | |
+ } BPB16_T; | |
+ | |
+ /* MS-DOS FAT32 BIOS parameter block (79 bytes) */ | |
+ typedef struct { | |
+ UINT8 sector_size[2]; | |
+ UINT8 sectors_per_clu; | |
+ UINT8 num_reserved[2]; | |
+ UINT8 num_fats; | |
+ UINT8 num_root_entries[2]; | |
+ UINT8 num_sectors[2]; | |
+ UINT8 media_type; | |
+ UINT8 num_fat_sectors[2]; | |
+ UINT8 sectors_in_track[2]; | |
+ UINT8 num_heads[2]; | |
+ UINT8 num_hid_sectors[4]; | |
+ UINT8 num_huge_sectors[4]; | |
+ UINT8 num_fat32_sectors[4]; | |
+ UINT8 ext_flags[2]; | |
+ UINT8 fs_version[2]; | |
+ UINT8 root_cluster[4]; | |
+ UINT8 fsinfo_sector[2]; | |
+ UINT8 backup_sector[2]; | |
+ UINT8 reserved[12]; | |
+ | |
+ UINT8 phy_drv_no; | |
+ UINT8 ext_reserved; | |
+ UINT8 ext_signature; | |
+ UINT8 vol_serial[4]; | |
+ UINT8 vol_label[11]; | |
+ UINT8 vol_type[8]; | |
+ } BPB32_T; | |
+ | |
+ /* MS-DOS EXFAT BIOS parameter block (109 bytes) */ | |
+ typedef struct { | |
+ UINT8 reserved1[53]; | |
+ UINT8 vol_offset[8]; | |
+ UINT8 vol_length[8]; | |
+ UINT8 fat_offset[4]; | |
+ UINT8 fat_length[4]; | |
+ UINT8 clu_offset[4]; | |
+ UINT8 clu_count[4]; | |
+ UINT8 root_cluster[4]; | |
+ UINT8 vol_serial[4]; | |
+ UINT8 fs_version[2]; | |
+ UINT8 vol_flags[2]; | |
+ UINT8 sector_size_bits; | |
+ UINT8 sectors_per_clu_bits; | |
+ UINT8 num_fats; | |
+ UINT8 phy_drv_no; | |
+ UINT8 perc_in_use; | |
+ UINT8 reserved2[7]; | |
+ } BPBEX_T; | |
+ | |
+ /* MS-DOS FAT file system information sector (512 bytes) */ | |
+ typedef struct { | |
+ UINT8 signature1[4]; // aligned | |
+ UINT8 reserved1[480]; | |
+ UINT8 signature2[4]; // aligned | |
+ UINT8 free_cluster[4]; // aligned | |
+ UINT8 next_cluster[4]; // aligned | |
+ UINT8 reserved2[14]; | |
+ UINT8 signature3[2]; | |
+ } FSI_SECTOR_T; | |
+ | |
+ /* MS-DOS FAT directory entry (32 bytes) */ | |
+ typedef struct { | |
+ UINT8 dummy[32]; | |
+ } DENTRY_T; | |
+ | |
+ typedef struct { | |
+ UINT8 name[DOS_NAME_LENGTH]; | |
+ UINT8 attr; | |
+ UINT8 lcase; | |
+ UINT8 create_time_ms; | |
+ UINT8 create_time[2]; // aligned | |
+ UINT8 create_date[2]; // aligned | |
+ UINT8 access_date[2]; // aligned | |
+ UINT8 start_clu_hi[2]; // aligned | |
+ UINT8 modify_time[2]; // aligned | |
+ UINT8 modify_date[2]; // aligned | |
+ UINT8 start_clu_lo[2]; // aligned | |
+ UINT8 size[4]; // aligned | |
+ } DOS_DENTRY_T; | |
+ | |
+ /* MS-DOS FAT extended directory entry (32 bytes) */ | |
+ typedef struct { | |
+ UINT8 order; | |
+ UINT8 unicode_0_4[10]; | |
+ UINT8 attr; | |
+ UINT8 sysid; | |
+ UINT8 checksum; | |
+ UINT8 unicode_5_10[12]; // aligned | |
+ UINT8 start_clu[2]; // aligned | |
+ UINT8 unicode_11_12[4]; // aligned | |
+ } EXT_DENTRY_T; | |
+ | |
+ /* MS-DOS EXFAT file directory entry (32 bytes) */ | |
+ typedef struct { | |
+ UINT8 type; | |
+ UINT8 num_ext; | |
+ UINT8 checksum[2]; // aligned | |
+ UINT8 attr[2]; // aligned | |
+ UINT8 reserved1[2]; | |
+ UINT8 create_time[2]; // aligned | |
+ UINT8 create_date[2]; // aligned | |
+ UINT8 modify_time[2]; // aligned | |
+ UINT8 modify_date[2]; // aligned | |
+ UINT8 access_time[2]; // aligned | |
+ UINT8 access_date[2]; // aligned | |
+ UINT8 create_time_ms; | |
+ UINT8 modify_time_ms; | |
+ UINT8 access_time_ms; | |
+ UINT8 reserved2[9]; | |
+ } FILE_DENTRY_T; | |
+ | |
+ /* MS-DOS EXFAT stream extension directory entry (32 bytes) */ | |
+ typedef struct { | |
+ UINT8 type; | |
+ UINT8 flags; | |
+ UINT8 reserved1; | |
+ UINT8 name_len; | |
+ UINT8 name_hash[2]; // aligned | |
+ UINT8 reserved2[2]; | |
+ UINT8 valid_size[8]; // aligned | |
+ UINT8 reserved3[4]; // aligned | |
+ UINT8 start_clu[4]; // aligned | |
+ UINT8 size[8]; // aligned | |
+ } STRM_DENTRY_T; | |
+ | |
+ /* MS-DOS EXFAT file name directory entry (32 bytes) */ | |
+ typedef struct { | |
+ UINT8 type; | |
+ UINT8 flags; | |
+ UINT8 unicode_0_14[30]; // aligned | |
+ } NAME_DENTRY_T; | |
+ | |
+ /* MS-DOS EXFAT allocation bitmap directory entry (32 bytes) */ | |
+ typedef struct { | |
+ UINT8 type; | |
+ UINT8 flags; | |
+ UINT8 reserved[18]; | |
+ UINT8 start_clu[4]; // aligned | |
+ UINT8 size[8]; // aligned | |
+ } BMAP_DENTRY_T; | |
+ | |
+ /* MS-DOS EXFAT up-case table directory entry (32 bytes) */ | |
+ typedef struct { | |
+ UINT8 type; | |
+ UINT8 reserved1[3]; | |
+ UINT8 checksum[4]; // aligned | |
+ UINT8 reserved2[12]; | |
+ UINT8 start_clu[4]; // aligned | |
+ UINT8 size[8]; // aligned | |
+ } CASE_DENTRY_T; | |
+ | |
+ /* MS-DOS EXFAT volume label directory entry (32 bytes) */ | |
+ typedef struct { | |
+ UINT8 type; | |
+ UINT8 label_len; | |
+ UINT8 unicode_0_10[22]; // aligned | |
+ UINT8 reserved[8]; | |
+ } VOLM_DENTRY_T; | |
+ | |
+ /* unused entry hint information */ | |
+ typedef struct { | |
+ UINT32 dir; | |
+ INT32 entry; | |
+ CHAIN_T clu; | |
+ } UENTRY_T; | |
+ | |
+ /* file system volume information structure */ | |
+ typedef struct __FS_STRUCT_T { | |
+ UINT32 mounted; | |
+ struct super_block *sb; | |
+ struct semaphore v_sem; | |
+ } FS_STRUCT_T; | |
+ | |
+ typedef struct { | |
+ INT32 (*alloc_cluster)(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain); | |
+ void (*free_cluster)(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse); | |
+ INT32 (*count_used_clusters)(struct super_block *sb); | |
+ | |
+ INT32 (*init_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, | |
+ UINT32 start_clu, UINT64 size); | |
+ INT32 (*init_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, | |
+ UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); | |
+ INT32 (*find_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type); | |
+ void (*delete_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 offset, INT32 num_entries); | |
+ void (*get_uni_name_from_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname); | |
+ INT32 (*count_ext_entries)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry); | |
+ INT32 (*calc_num_entries)(UNI_NAME_T *p_uniname); | |
+ | |
+ UINT32 (*get_entry_type)(DENTRY_T *p_entry); | |
+ void (*set_entry_type)(DENTRY_T *p_entry, UINT32 type); | |
+ UINT32 (*get_entry_attr)(DENTRY_T *p_entry); | |
+ void (*set_entry_attr)(DENTRY_T *p_entry, UINT32 attr); | |
+ UINT8 (*get_entry_flag)(DENTRY_T *p_entry); | |
+ void (*set_entry_flag)(DENTRY_T *p_entry, UINT8 flag); | |
+ UINT32 (*get_entry_clu0)(DENTRY_T *p_entry); | |
+ void (*set_entry_clu0)(DENTRY_T *p_entry, UINT32 clu0); | |
+ UINT64 (*get_entry_size)(DENTRY_T *p_entry); | |
+ void (*set_entry_size)(DENTRY_T *p_entry, UINT64 size); | |
+ void (*get_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); | |
+ void (*set_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); | |
+ } FS_FUNC_T; | |
+ | |
+ typedef struct __FS_INFO_T { | |
+ UINT32 drv; // drive ID | |
+ UINT32 vol_type; // volume FAT type | |
+ UINT32 vol_id; // volume serial number | |
+ | |
+ UINT32 num_sectors; // num of sectors in volume | |
+ UINT32 num_clusters; // num of clusters in volume | |
+ UINT32 cluster_size; // cluster size in bytes | |
+ UINT32 cluster_size_bits; | |
+ UINT32 sectors_per_clu; // cluster size in sectors | |
+ UINT32 sectors_per_clu_bits; | |
+ | |
+ UINT32 PBR_sector; // PBR sector | |
+ UINT32 FAT1_start_sector; // FAT1 start sector | |
+ UINT32 FAT2_start_sector; // FAT2 start sector | |
+ UINT32 root_start_sector; // root dir start sector | |
+ UINT32 data_start_sector; // data area start sector | |
+ UINT32 num_FAT_sectors; // num of FAT sectors | |
+ | |
+ UINT32 root_dir; // root dir cluster | |
+ UINT32 dentries_in_root; // num of dentries in root dir | |
+ UINT32 dentries_per_clu; // num of dentries per cluster | |
+ | |
+ UINT32 vol_flag; // volume dirty flag | |
+ struct buffer_head *pbr_bh; // PBR sector | |
+ | |
+ UINT32 map_clu; // allocation bitmap start cluster | |
+ UINT32 map_sectors; // num of allocation bitmap sectors | |
+ struct buffer_head **vol_amap; // allocation bitmap | |
+ | |
+ UINT16 **vol_utbl; // upcase table | |
+ | |
+ UINT32 clu_srch_ptr; // cluster search pointer | |
+ UINT32 used_clusters; // number of used clusters | |
+ UENTRY_T hint_uentry; // unused entry hint information | |
+ | |
+ UINT32 dev_ejected; // block device operation error flag | |
+ | |
+ FS_FUNC_T *fs_func; | |
+ | |
+ /* 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 { | |
+ UINT32 sector; // sector number that contains file_entry | |
+ INT32 offset; // byte offset in the sector | |
+ INT32 alloc_flag; // flag in stream entry. 01 for cluster chain, 03 for contig. clusteres. | |
+ UINT32 num_entries; | |
+ | |
+ // __buf should be the last member | |
+ void *__buf; | |
+ } ENTRY_SET_CACHE_T; | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* External Function Declarations */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ /* file system initialization & shutdown functions */ | |
+ INT32 ffsInit(void); | |
+ INT32 ffsShutdown(void); | |
+ | |
+ /* volume management functions */ | |
+ INT32 ffsMountVol(struct super_block *sb, INT32 drv); | |
+ INT32 ffsUmountVol(struct super_block *sb); | |
+ INT32 ffsCheckVol(struct super_block *sb); | |
+ INT32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info); | |
+ INT32 ffsSyncVol(struct super_block *sb, INT32 do_sync); | |
+ | |
+ /* file management functions */ | |
+ INT32 ffsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid); | |
+ INT32 ffsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid); | |
+ INT32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount); | |
+ INT32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount); | |
+ INT32 ffsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size); | |
+ INT32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); | |
+ INT32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid); | |
+ INT32 ffsSetAttr(struct inode *inode, UINT32 attr); | |
+ INT32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info); | |
+ INT32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info); | |
+ INT32 ffsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu); | |
+ | |
+ /* directory management functions */ | |
+ INT32 ffsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid); | |
+ INT32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_ent); | |
+ INT32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid); | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* External Function Declarations (NOT TO UPPER LAYER) */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ /* fs management functions */ | |
+ INT32 fs_init(void); | |
+ INT32 fs_shutdown(void); | |
+ void fs_set_vol_flags(struct super_block *sb, UINT32 new_flag); | |
+ void fs_sync(struct super_block *sb, INT32 do_sync); | |
+ void fs_error(struct super_block *sb); | |
+ | |
+ /* cluster management functions */ | |
+ INT32 clear_cluster(struct super_block *sb, UINT32 clu); | |
+ INT32 fat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain); | |
+ INT32 exfat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain); | |
+ void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse); | |
+ void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse); | |
+ UINT32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain); | |
+ INT32 count_num_clusters(struct super_block *sb, CHAIN_T *dir); | |
+ INT32 fat_count_used_clusters(struct super_block *sb); | |
+ INT32 exfat_count_used_clusters(struct super_block *sb); | |
+ void exfat_chain_cont_cluster(struct super_block *sb, UINT32 chain, INT32 len); | |
+ | |
+ /* allocation bitmap management functions */ | |
+ INT32 load_alloc_bitmap(struct super_block *sb); | |
+ void free_alloc_bitmap(struct super_block *sb); | |
+ INT32 set_alloc_bitmap(struct super_block *sb, UINT32 clu); | |
+ INT32 clr_alloc_bitmap(struct super_block *sb, UINT32 clu); | |
+ UINT32 test_alloc_bitmap(struct super_block *sb, UINT32 clu); | |
+ void sync_alloc_bitmap(struct super_block *sb); | |
+ | |
+ /* upcase table management functions */ | |
+ INT32 load_upcase_table(struct super_block *sb); | |
+ void free_upcase_table(struct super_block *sb); | |
+ | |
+ /* dir entry management functions */ | |
+ UINT32 fat_get_entry_type(DENTRY_T *p_entry); | |
+ UINT32 exfat_get_entry_type(DENTRY_T *p_entry); | |
+ void fat_set_entry_type(DENTRY_T *p_entry, UINT32 type); | |
+ void exfat_set_entry_type(DENTRY_T *p_entry, UINT32 type); | |
+ UINT32 fat_get_entry_attr(DENTRY_T *p_entry); | |
+ UINT32 exfat_get_entry_attr(DENTRY_T *p_entry); | |
+ void fat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr); | |
+ void exfat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr); | |
+ UINT8 fat_get_entry_flag(DENTRY_T *p_entry); | |
+ UINT8 exfat_get_entry_flag(DENTRY_T *p_entry); | |
+ void fat_set_entry_flag(DENTRY_T *p_entry, UINT8 flag); | |
+ void exfat_set_entry_flag(DENTRY_T *p_entry, UINT8 flag); | |
+ UINT32 fat_get_entry_clu0(DENTRY_T *p_entry); | |
+ UINT32 exfat_get_entry_clu0(DENTRY_T *p_entry); | |
+ void fat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu); | |
+ void exfat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu); | |
+ UINT64 fat_get_entry_size(DENTRY_T *p_entry); | |
+ UINT64 exfat_get_entry_size(DENTRY_T *p_entry); | |
+ void fat_set_entry_size(DENTRY_T *p_entry, UINT64 size); | |
+ void exfat_set_entry_size(DENTRY_T *p_entry, UINT64 size); | |
+ void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); | |
+ void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); | |
+ void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); | |
+ void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); | |
+ INT32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, UINT32 start_clu, UINT64 size); | |
+ INT32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, UINT32 start_clu, UINT64 size); | |
+ INT32 fat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); | |
+ INT32 exfat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); | |
+ void init_dos_entry(DOS_DENTRY_T *ep, UINT32 type, UINT32 start_clu); | |
+ void init_ext_entry(EXT_DENTRY_T *ep, INT32 order, UINT8 chksum, UINT16 *uniname); | |
+ void init_file_entry(FILE_DENTRY_T *ep, UINT32 type); | |
+ void init_strm_entry(STRM_DENTRY_T *ep, UINT8 flags, UINT32 start_clu, UINT64 size); | |
+ void init_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname); | |
+ void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries); | |
+ void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries); | |
+ | |
+ INT32 find_location(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector, INT32 *offset); | |
+ DENTRY_T *get_entry_with_sector(struct super_block *sb, UINT32 sector, INT32 offset); | |
+ DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector); | |
+ ENTRY_SET_CACHE_T *get_entry_set_in_dir (struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, DENTRY_T **file_ep); | |
+ void release_entry_set (ENTRY_SET_CACHE_T *es); | |
+ INT32 write_whole_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es); | |
+ INT32 write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, UINT32 count); | |
+ INT32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 num_entries); | |
+ INT32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, INT32 num_entries); | |
+ INT32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type); | |
+ INT32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type); | |
+ INT32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry); | |
+ INT32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry); | |
+ INT32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, UINT32 type); | |
+ void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, INT32 entry); | |
+ void update_dir_checksum_with_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es); | |
+ BOOL is_dir_empty(struct super_block *sb, CHAIN_T *p_dir); | |
+ | |
+ /* name conversion functions */ | |
+ INT32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 *entries, DOS_NAME_T *p_dosname); | |
+ void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, UINT8 mode); | |
+ void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname); | |
+ void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname); | |
+ INT32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, UINT16 *uniname, INT32 order); | |
+ INT32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname, INT32 order); | |
+ INT32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname); | |
+ void fat_attach_count_to_dos_name(UINT8 *dosname, INT32 count); | |
+ INT32 fat_calc_num_entries(UNI_NAME_T *p_uniname); | |
+ INT32 exfat_calc_num_entries(UNI_NAME_T *p_uniname); | |
+ UINT8 calc_checksum_1byte(void *data, INT32 len, UINT8 chksum); | |
+ UINT16 calc_checksum_2byte(void *data, INT32 len, UINT16 chksum, INT32 type); | |
+ UINT32 calc_checksum_4byte(void *data, INT32 len, UINT32 chksum, INT32 type); | |
+ | |
+ /* name resolution functions */ | |
+ INT32 resolve_path(struct inode *inode, UINT8 *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname); | |
+ INT32 resolve_name(UINT8 *name, UINT8 **arg); | |
+ | |
+ /* file operation functions */ | |
+ INT32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); | |
+ INT32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); | |
+ INT32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); | |
+ INT32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid); | |
+ INT32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, UINT8 mode, FILE_ID_T *fid); | |
+ void remove_file(struct inode *inode, CHAIN_T *p_dir, INT32 entry); | |
+ INT32 rename_file(struct inode *inode, CHAIN_T *p_dir, INT32 old_entry, UNI_NAME_T *p_uniname, FILE_ID_T *fid); | |
+ INT32 move_file(struct inode *inode, CHAIN_T *p_olddir, INT32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid); | |
+ | |
+ /* sector read/write functions */ | |
+ INT32 sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 read); | |
+ INT32 sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 sync); | |
+ INT32 multi_sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 num_secs, INT32 read); | |
+ INT32 multi_sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 num_secs, INT32 sync); | |
+ | |
+#ifdef __cplusplus | |
+} | |
+#endif /* __cplusplus */ | |
+ | |
+#endif /* _EXFAT_H */ | |
+ | |
+/* end of exfat.h */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_nls.c exfat_kernel-2.6.32/fs/exfat/exfat_nls.c | |
--- kernel-2.6.32/fs/exfat/exfat_nls.c 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_nls.c 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,394 @@ | |
+/* | |
+ * 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_global.h" | |
+#include "exfat_data.h" | |
+ | |
+#include "exfat_nls.h" | |
+#include "exfat_api.h" | |
+#include "exfat_super.h" | |
+#include "exfat.h" | |
+ | |
+#include <linux/nls.h> | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Global Variable Definitions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Local Variable Definitions */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+static UINT16 bad_dos_chars[] = { | |
+ /* + , ; = [ ] */ | |
+ 0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D, | |
+ 0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D, | |
+ 0 | |
+}; | |
+ | |
+static UINT16 bad_uni_chars[] = { | |
+ /* " * / : < > ? \ | */ | |
+ 0x0022, 0x002A, 0x002F, 0x003A, | |
+ 0x003C, 0x003E, 0x003F, 0x005C, 0x007C, | |
+ 0 | |
+}; | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* Local Function Declarations */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy); | |
+static INT32 convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy); | |
+ | |
+/*======================================================================*/ | |
+/* Global Function Definitions */ | |
+/*======================================================================*/ | |
+ | |
+UINT16 nls_upper(struct super_block *sb, UINT16 a) | |
+{ | |
+ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); | |
+ | |
+ if (EXFAT_SB(sb)->options.casesensitive) | |
+ return(a); | |
+ if (p_fs->vol_utbl != 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; | |
+} | |
+ | |
+INT32 nls_dosname_cmp(struct super_block *sb, UINT8 *a, UINT8 *b) | |
+{ | |
+ return(STRNCMP((void *) a, (void *) b, DOS_NAME_LENGTH)); | |
+} /* end of nls_dosname_cmp */ | |
+ | |
+INT32 nls_uniname_cmp(struct super_block *sb, UINT16 *a, UINT16 *b) | |
+{ | |
+ INT32 i; | |
+ | |
+ for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) { | |
+ if (nls_upper(sb, *a) != nls_upper(sb, *b)) return(1); | |
+ if (*a == 0x0) return(0); | |
+ } | |
+ return(0); | |
+} /* end of nls_uniname_cmp */ | |
+ | |
+void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, INT32 *p_lossy) | |
+{ | |
+ INT32 i, j, len, lossy = FALSE; | |
+ UINT8 buf[MAX_CHARSET_SIZE]; | |
+ UINT8 lower = 0, upper = 0; | |
+ UINT8 *dosname = p_dosname->name; | |
+ UINT16 *uniname = p_uniname->name; | |
+ UINT16 *p, *last_period; | |
+ struct nls_table *nls = EXFAT_SB(sb)->nls_disk; | |
+ | |
+ for (i = 0; i < DOS_NAME_LENGTH; i++) { | |
+ *(dosname+i) = ' '; | |
+ } | |
+ | |
+ if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_CUR_DIR_NAME)) { | |
+ *(dosname) = '.'; | |
+ p_dosname->name_case = 0x0; | |
+ if (p_lossy != NULL) *p_lossy = FALSE; | |
+ return; | |
+ } | |
+ | |
+ if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_PAR_DIR_NAME)) { | |
+ *(dosname) = '.'; | |
+ *(dosname+1) = '.'; | |
+ p_dosname->name_case = 0x0; | |
+ if (p_lossy != NULL) *p_lossy = FALSE; | |
+ return; | |
+ } | |
+ | |
+ /* search for the last embedded period */ | |
+ last_period = NULL; | |
+ for (p = uniname; *p; p++) { | |
+ if (*p == (UINT16) '.') last_period = p; | |
+ } | |
+ | |
+ i = 0; | |
+ while (i < DOS_NAME_LENGTH) { | |
+ if (i == 8) { | |
+ if (last_period == NULL) break; | |
+ | |
+ if (uniname <= last_period) { | |
+ if (uniname < last_period) lossy = TRUE; | |
+ uniname = last_period + 1; | |
+ } | |
+ } | |
+ | |
+ if (*uniname == (UINT16) '\0') { | |
+ break; | |
+ } else if (*uniname == (UINT16) ' ') { | |
+ lossy = TRUE; | |
+ } else if (*uniname == (UINT16) '.') { | |
+ if (uniname < last_period) lossy = TRUE; | |
+ else i = 8; | |
+ } else if (WSTRCHR(bad_dos_chars, *uniname)) { | |
+ lossy = TRUE; | |
+ *(dosname+i) = '_'; | |
+ i++; | |
+ } else { | |
+ len = convert_uni_to_ch(nls, buf, *uniname, &lossy); | |
+ | |
+ if (len > 1) { | |
+ if ((i >= 8) && ((i+len) > DOS_NAME_LENGTH)) { | |
+ break; | |
+ } | |
+ if ((i < 8) && ((i+len) > 8)) { | |
+ i = 8; | |
+ continue; | |
+ } | |
+ | |
+ lower = 0xFF; | |
+ | |
+ for (j = 0; j < len; j++, i++) { | |
+ *(dosname+i) = *(buf+j); | |
+ } | |
+ } else { /* 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) | |
+{ | |
+ INT32 i = 0, j, n = 0; | |
+ UINT8 buf[DOS_NAME_LENGTH+2]; | |
+ UINT8 *dosname = p_dosname->name; | |
+ UINT16 *uniname = p_uniname->name; | |
+ struct nls_table *nls = EXFAT_SB(sb)->nls_disk; | |
+ | |
+ if (*dosname == 0x05) { | |
+ *buf = 0xE5; | |
+ i++; | |
+ n++; | |
+ } | |
+ | |
+ for ( ; i < 8; i++, n++) { | |
+ if (*(dosname+i) == ' ') break; | |
+ | |
+ if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x08)) | |
+ *(buf+n) = *(dosname+i) + ('a' - 'A'); | |
+ else | |
+ *(buf+n) = *(dosname+i); | |
+ } | |
+ if (*(dosname+8) != ' ') { | |
+ *(buf+n) = '.'; | |
+ n++; | |
+ } | |
+ | |
+ for (i = 8; i < DOS_NAME_LENGTH; i++, n++) { | |
+ if (*(dosname+i) == ' ') break; | |
+ | |
+ if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x10)) | |
+ *(buf+n) = *(dosname+i) + ('a' - 'A'); | |
+ else | |
+ *(buf+n) = *(dosname+i); | |
+ } | |
+ *(buf+n) = '\0'; | |
+ | |
+ i = j = 0; | |
+ while (j < (MAX_NAME_LENGTH-1)) { | |
+ if (*(buf+i) == '\0') break; | |
+ | |
+ i += convert_ch_to_uni(nls, uniname, (buf+i), NULL); | |
+ | |
+ uniname++; | |
+ j++; | |
+ } | |
+ | |
+ *uniname = (UINT16) '\0'; | |
+} /* end of nls_dosname_to_uniname */ | |
+ | |
+void nls_uniname_to_cstring(struct super_block *sb, UINT8 *p_cstring, UNI_NAME_T *p_uniname) | |
+{ | |
+ INT32 i, j, len; | |
+ UINT8 buf[MAX_CHARSET_SIZE]; | |
+ UINT16 *uniname = p_uniname->name; | |
+ struct nls_table *nls = EXFAT_SB(sb)->nls_io; | |
+ | |
+ i = 0; | |
+ while (i < (MAX_NAME_LENGTH-1)) { | |
+ if (*uniname == (UINT16) '\0') break; | |
+ | |
+ len = convert_uni_to_ch(nls, buf, *uniname, NULL); | |
+ | |
+ if (len > 1) { | |
+ for (j = 0; j < len; j++) | |
+ *p_cstring++ = (INT8) *(buf+j); | |
+ } else { /* len == 1 */ | |
+ *p_cstring++ = (INT8) *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, UINT8 *p_cstring, INT32 *p_lossy) | |
+{ | |
+ INT32 i, j, lossy = FALSE; | |
+ UINT8 *end_of_name; | |
+ UINT8 upname[MAX_NAME_LENGTH * 2]; | |
+ UINT16 *uniname = p_uniname->name; | |
+ struct nls_table *nls = EXFAT_SB(sb)->nls_io; | |
+ | |
+ | |
+ /* strip all trailing spaces */ | |
+ end_of_name = p_cstring + STRLEN((INT8 *) p_cstring); | |
+ | |
+ while (*(--end_of_name) == ' ') { | |
+ if (end_of_name < p_cstring) break; | |
+ } | |
+ *(++end_of_name) = '\0'; | |
+ | |
+ if (STRCMP((INT8 *) p_cstring, ".") && STRCMP((INT8 *) p_cstring, "..")) { | |
+ | |
+ /* strip all trailing periods */ | |
+ while (*(--end_of_name) == '.') { | |
+ if (end_of_name < p_cstring) break; | |
+ } | |
+ *(++end_of_name) = '\0'; | |
+ } | |
+ | |
+ if (*p_cstring == '\0') | |
+ lossy = TRUE; | |
+ | |
+ i = j = 0; | |
+ while (j < (MAX_NAME_LENGTH-1)) { | |
+ if (*(p_cstring+i) == '\0') break; | |
+ | |
+ i += convert_ch_to_uni(nls, uniname, (UINT8 *)(p_cstring+i), &lossy); | |
+ | |
+ if ((*uniname < 0x0020) || WSTRCHR(bad_uni_chars, *uniname)) | |
+ lossy = TRUE; | |
+ | |
+ SET16_A(upname + j * 2, nls_upper(sb, *uniname)); | |
+ | |
+ uniname++; | |
+ j++; | |
+ } | |
+ | |
+ if (*(p_cstring+i) != '\0') | |
+ lossy = TRUE; | |
+ *uniname = (UINT16) '\0'; | |
+ | |
+ p_uniname->name_len = j; | |
+ p_uniname->name_hash = calc_checksum_2byte((void *) upname, j<<1, 0, CS_DEFAULT); | |
+ | |
+ if (p_lossy != NULL) | |
+ *p_lossy = lossy; | |
+} /* end of nls_cstring_to_uniname */ | |
+ | |
+/*======================================================================*/ | |
+/* Local Function Definitions */ | |
+/*======================================================================*/ | |
+ | |
+static INT32 convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy) | |
+{ | |
+ int len; | |
+ | |
+ *uni = 0x0; | |
+ | |
+ if (ch[0] < 0x80) { | |
+ *uni = (UINT16) ch[0]; | |
+ return(1); | |
+ } | |
+ | |
+ if ((len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni)) < 0) { | |
+ /* conversion failed */ | |
+ printk("%s: fail to use nls \n", __func__); | |
+ if (lossy != NULL) | |
+ *lossy = TRUE; | |
+ *uni = (UINT16) '_'; | |
+ if (!strcmp(nls->charset, "utf8")) return(1); | |
+ else return(2); | |
+ } | |
+ | |
+ return(len); | |
+} /* end of convert_ch_to_uni */ | |
+ | |
+static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy) | |
+{ | |
+ int len; | |
+ | |
+ ch[0] = 0x0; | |
+ | |
+ if (uni < 0x0080) { | |
+ ch[0] = (UINT8) uni; | |
+ return(1); | |
+ } | |
+ | |
+ if ((len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE)) < 0) { | |
+ /* 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 */ | |
+ | |
+/* end of exfat_nls.c */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_nls.h exfat_kernel-2.6.32/fs/exfat/exfat_nls.h | |
--- kernel-2.6.32/fs/exfat/exfat_nls.h 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_nls.h 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,101 @@ | |
+/* | |
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. | |
+ * | |
+ * This program is free software; you can redistribute it and/or | |
+ * modify it under the terms of the GNU General Public License | |
+ * as published by the Free Software Foundation; either version 2 | |
+ * of the License, or (at your option) any later version. | |
+ * | |
+ * This program is distributed in the hope that it will be useful, | |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
+ * GNU General Public License for more details. | |
+ * | |
+ * You should have received a copy of the GNU General Public License | |
+ * along with this program; if not, write to the Free Software | |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
+ */ | |
+ | |
+/************************************************************************/ | |
+/* */ | |
+/* PROJECT : exFAT & FAT12/16/32 File System */ | |
+/* FILE : exfat_nls.h */ | |
+/* PURPOSE : Header File for exFAT NLS Manager */ | |
+/* */ | |
+/*----------------------------------------------------------------------*/ | |
+/* NOTES */ | |
+/* */ | |
+/*----------------------------------------------------------------------*/ | |
+/* REVISION HISTORY (Ver 0.9) */ | |
+/* */ | |
+/* - 2010.11.15 [Joosun Hahn] : first writing */ | |
+/* */ | |
+/************************************************************************/ | |
+ | |
+#ifndef _EXFAT_NLS_H | |
+#define _EXFAT_NLS_H | |
+ | |
+#include <linux/nls.h> | |
+ | |
+#include "exfat_config.h" | |
+#include "exfat_global.h" | |
+#include "exfat_api.h" | |
+ | |
+#ifdef __cplusplus | |
+extern "C" { | |
+#endif /* __cplusplus */ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* 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 { | |
+ UINT8 name[DOS_NAME_LENGTH]; | |
+ UINT8 name_case; | |
+} DOS_NAME_T; | |
+ | |
+/* unicode name stucture */ | |
+typedef struct { | |
+ UINT16 name[MAX_NAME_LENGTH]; | |
+ UINT16 name_hash; | |
+ UINT8 name_len; | |
+} UNI_NAME_T; | |
+ | |
+/*----------------------------------------------------------------------*/ | |
+/* External Function Declarations */ | |
+/*----------------------------------------------------------------------*/ | |
+ | |
+/* NLS management function */ | |
+UINT16 nls_upper(struct super_block *sb, UINT16 a); | |
+INT32 nls_dosname_cmp(struct super_block *sb, UINT8 *a, UINT8 *b); | |
+INT32 nls_uniname_cmp(struct super_block *sb, UINT16 *a, UINT16 *b); | |
+void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, INT32 *p_lossy); | |
+void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); | |
+void nls_uniname_to_cstring(struct super_block *sb, UINT8 *p_cstring, UNI_NAME_T *p_uniname); | |
+void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, UINT8 *p_cstring, INT32 *p_lossy); | |
+ | |
+#ifdef __cplusplus | |
+} | |
+#endif /* __cplusplus */ | |
+ | |
+#endif /* _EXFAT_NLS_H */ | |
+ | |
+/* end of exfat_nls.h */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_oal.c exfat_kernel-2.6.32/fs/exfat/exfat_oal.c | |
--- kernel-2.6.32/fs/exfat/exfat_oal.c 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_oal.c 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,189 @@ | |
+/* Some of the source code in this file came from "linux/fs/fat/misc.c". */ | |
+/* | |
+ * linux/fs/fat/misc.c | |
+ * | |
+ * Written 1992,1993 by Werner Almesberger | |
+ * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 | |
+ * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) | |
+ */ | |
+ | |
+/* | |
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. | |
+ * | |
+ * This program is free software; you can redistribute it and/or | |
+ * modify it under the terms of the GNU General Public License | |
+ * as published by the Free Software Foundation; either version 2 | |
+ * of the License, or (at your option) any later version. | |
+ * | |
+ * This program is distributed in the hope that it will be useful, | |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
+ * GNU General Public License for more details. | |
+ * | |
+ * You should have received a copy of the GNU General Public License | |
+ * along with this program; if not, write to the Free Software | |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
+ */ | |
+ | |
+/************************************************************************/ | |
+/* */ | |
+/* PROJECT : exFAT & FAT12/16/32 File System */ | |
+/* FILE : exfat_oal.c */ | |
+/* PURPOSE : exFAT OS Adaptation Layer */ | |
+/* (Semaphore Functions & Real-Time Clock Functions) */ | |
+/* */ | |
+/*----------------------------------------------------------------------*/ | |
+/* NOTES */ | |
+/* */ | |
+/*----------------------------------------------------------------------*/ | |
+/* REVISION HISTORY (Ver 0.9) */ | |
+/* */ | |
+/* - 2010.11.15 [Joosun Hahn] : first writing */ | |
+/* */ | |
+/************************************************************************/ | |
+ | |
+#include <linux/semaphore.h> | |
+#include <linux/time.h> | |
+ | |
+#include "exfat_config.h" | |
+#include "exfat_global.h" | |
+#include "exfat_api.h" | |
+#include "exfat_oal.h" | |
+ | |
+/*======================================================================*/ | |
+/* */ | |
+/* SEMAPHORE FUNCTIONS */ | |
+/* */ | |
+/*======================================================================*/ | |
+ | |
+DECLARE_MUTEX(z_sem); | |
+ | |
+INT32 sm_init(struct semaphore *sm) | |
+{ | |
+ sema_init(sm, 1); | |
+ return(0); | |
+} /* end of sm_init */ | |
+ | |
+INT32 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 = CURRENT_TIME_SEC; | |
+ time_t second = ts.tv_sec; | |
+ time_t day, leap_day, month, year; | |
+ | |
+ second -= sys_tz.tz_minuteswest * SECS_PER_MIN; | |
+ | |
+ /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ | |
+ if (second < UNIX_SECS_1980) { | |
+ tp->sec = 0; | |
+ tp->min = 0; | |
+ tp->hour = 0; | |
+ tp->day = 1; | |
+ tp->mon = 1; | |
+ tp->year = 0; | |
+ return(tp); | |
+ } | |
+#if BITS_PER_LONG == 64 | |
+ if (second >= UNIX_SECS_2108) { | |
+ tp->sec = 59; | |
+ tp->min = 59; | |
+ tp->hour = 23; | |
+ tp->day = 31; | |
+ tp->mon = 12; | |
+ tp->year = 127; | |
+ return(tp); | |
+ } | |
+#endif | |
+ | |
+ day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; | |
+ year = day / 365; | |
+ | |
+ MAKE_LEAP_YEAR(leap_day, year); | |
+ if (year * 365 + leap_day > day) | |
+ year--; | |
+ | |
+ MAKE_LEAP_YEAR(leap_day, year); | |
+ | |
+ day -= year * 365 + leap_day; | |
+ | |
+ if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { | |
+ month = 2; | |
+ } else { | |
+ if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) | |
+ day--; | |
+ for (month = 1; month < 12; month++) { | |
+ if (accum_days_in_year[month + 1] > day) | |
+ break; | |
+ } | |
+ } | |
+ day -= accum_days_in_year[month]; | |
+ | |
+ tp->sec = second % SECS_PER_MIN; | |
+ tp->min = (second / SECS_PER_MIN) % 60; | |
+ tp->hour = (second / SECS_PER_HOUR) % 24; | |
+ tp->day = day + 1; | |
+ tp->mon = month; | |
+ tp->year = year; | |
+ | |
+ return(tp); | |
+} /* end of tm_current */ | |
+ | |
+/* end of exfat_oal.c */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_oal.h exfat_kernel-2.6.32/fs/exfat/exfat_oal.h | |
--- kernel-2.6.32/fs/exfat/exfat_oal.h 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_oal.h 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,88 @@ | |
+/* | |
+ * 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 "exfat_config.h" | |
+#include "exfat_global.h" | |
+#include <linux/version.h> | |
+ | |
+#ifdef __cplusplus | |
+extern "C" { | |
+#endif /* __cplusplus */ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Constant & Macro Definitions (Configurable) */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Constant & Macro Definitions (Non-Configurable) */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Type Definitions */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ typedef struct { | |
+ UINT16 sec; /* 0 ~ 59 */ | |
+ UINT16 min; /* 0 ~ 59 */ | |
+ UINT16 hour; /* 0 ~ 23 */ | |
+ UINT16 day; /* 1 ~ 31 */ | |
+ UINT16 mon; /* 1 ~ 12 */ | |
+ UINT16 year; /* 0 ~ 127 (since 1980) */ | |
+ } TIMESTAMP_T; | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* External Function Declarations */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) | |
+#define DECLARE_MUTEX(m) DEFINE_SEMAPHORE(m) | |
+#endif | |
+ | |
+ INT32 sm_init(struct semaphore *sm); | |
+ INT32 sm_P(struct semaphore *sm); | |
+ void sm_V(struct semaphore *sm); | |
+ | |
+ TIMESTAMP_T *tm_current(TIMESTAMP_T *tm); | |
+ | |
+#ifdef __cplusplus | |
+} | |
+#endif /* __cplusplus */ | |
+ | |
+#endif /* _EXFAT_OAL_H */ | |
+ | |
+/* end of exfat_oal.h */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_part.h exfat_kernel-2.6.32/fs/exfat/exfat_part.h | |
--- kernel-2.6.32/fs/exfat/exfat_part.h 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_part.h 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,92 @@ | |
+/* | |
+ * 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_part.h */ | |
+/* PURPOSE : Header File for exFAT Partition Manager */ | |
+/* */ | |
+/*----------------------------------------------------------------------*/ | |
+/* NOTES */ | |
+/* */ | |
+/*----------------------------------------------------------------------*/ | |
+/* REVISION HISTORY (Ver 0.9) */ | |
+/* */ | |
+/* - 2010.11.15 [Joosun Hahn] : first writing */ | |
+/* */ | |
+/************************************************************************/ | |
+ | |
+#ifndef _EXFAT_PART_H | |
+#define _EXFAT_PART_H | |
+ | |
+#include "exfat_config.h" | |
+#include "exfat_global.h" | |
+#include "exfat_api.h" | |
+ | |
+#ifdef __cplusplus | |
+extern "C" { | |
+#endif /* __cplusplus */ | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Constant & Macro Definitions */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+#define MBR_SIGNATURE 0xAA55 | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* Type Definitions */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ /* MS-DOS FAT master boot record (512 bytes) */ | |
+ typedef struct { | |
+ UINT8 boot_code[446]; | |
+ UINT8 partition[64]; | |
+ UINT8 signature[2]; | |
+ } MBR_SECTOR_T; | |
+ | |
+ /* MS-DOS FAT partition table (64 bytes) */ | |
+ typedef struct { | |
+ UINT8 def_boot; | |
+ UINT8 bgn_chs[3]; | |
+ UINT8 sys_type; | |
+ UINT8 end_chs[3]; | |
+ UINT8 start_sector[4]; | |
+ UINT8 num_sectors[4]; | |
+ } PART_ENTRY_T; | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* External Function Declarations */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+ /* volume management functions */ | |
+ INT32 ffsSetPartition(INT32 dev, INT32 num_vol, PART_INFO_T *vol_spec); | |
+ INT32 ffsGetPartition(INT32 dev, INT32 *num_vol, PART_INFO_T *vol_spec); | |
+ INT32 ffsGetDevInfo(INT32 dev, DEV_INFO_T *info); | |
+ | |
+ /*----------------------------------------------------------------------*/ | |
+ /* External Function Declarations (NOT TO UPPER LAYER) */ | |
+ /*----------------------------------------------------------------------*/ | |
+ | |
+#ifdef __cplusplus | |
+} | |
+#endif /* __cplusplus */ | |
+ | |
+#endif /* _EXFAT_PART_H */ | |
+ | |
+/* end of exfat_part.h */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_super.c exfat_kernel-2.6.32/fs/exfat/exfat_super.c | |
--- kernel-2.6.32/fs/exfat/exfat_super.c 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_super.c 2013-09-01 13:03:20.000000000 +1100 | |
@@ -0,0 +1,2299 @@ | |
+/* Some of the source code in this file came from "linux/fs/fat/file.c","linux/fs/fat/inode.c" and "linux/fs/fat/misc.c". */ | |
+/* | |
+ * linux/fs/fat/file.c | |
+ * | |
+ * Written 1992,1993 by Werner Almesberger | |
+ * | |
+ * regular file handling primitives for fat-based filesystems | |
+ */ | |
+ | |
+/* | |
+ * linux/fs/fat/inode.c | |
+ * | |
+ * Written 1992,1993 by Werner Almesberger | |
+ * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner | |
+ * Rewritten for the constant inumbers support by Al Viro | |
+ * | |
+ * Fixes: | |
+ * | |
+ * Max Cohan: Fixed invalid FSINFO offset when info_sector is 0 | |
+ */ | |
+ | |
+/* | |
+ * linux/fs/fat/misc.c | |
+ * | |
+ * Written 1992,1993 by Werner Almesberger | |
+ * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 | |
+ * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) | |
+ */ | |
+ | |
+/* | |
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. | |
+ * | |
+ * This program is free software; you can redistribute it and/or | |
+ * modify it under the terms of the GNU General Public License | |
+ * as published by the Free Software Foundation; either version 2 | |
+ * of the License, or (at your option) any later version. | |
+ * | |
+ * This program is distributed in the hope that it will be useful, | |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
+ * GNU General Public License for more details. | |
+ * | |
+ * You should have received a copy of the GNU General Public License | |
+ * along with this program; if not, write to the Free Software | |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
+ */ | |
+ | |
+#include <linux/version.h> | |
+#include <linux/module.h> | |
+#include <linux/init.h> | |
+#include <linux/time.h> | |
+#include <linux/slab.h> | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) | |
+#include <linux/smp_lock.h> | |
+#endif | |
+#include <linux/seq_file.h> | |
+#include <linux/pagemap.h> | |
+#include <linux/mpage.h> | |
+#include <linux/buffer_head.h> | |
+#include <linux/exportfs.h> | |
+#include <linux/mount.h> | |
+#include <linux/vfs.h> | |
+#include <linux/parser.h> | |
+#include <linux/uio.h> | |
+#include <linux/writeback.h> | |
+#include <linux/log2.h> | |
+#include <linux/hash.h> | |
+#include <linux/backing-dev.h> | |
+#include <linux/sched.h> | |
+#include <linux/fs_struct.h> | |
+#include <linux/namei.h> | |
+#include <asm/current.h> | |
+#include <asm/unaligned.h> | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) | |
+#include <linux/aio.h> | |
+#endif | |
+ | |
+#include "exfat_version.h" | |
+#include "exfat_config.h" | |
+#include "exfat_global.h" | |
+#include "exfat_data.h" | |
+#include "exfat_oal.h" | |
+ | |
+#include "exfat_blkdev.h" | |
+#include "exfat_cache.h" | |
+#include "exfat_part.h" | |
+#include "exfat_nls.h" | |
+#include "exfat_api.h" | |
+#include "exfat.h" | |
+ | |
+#include "exfat_super.h" | |
+ | |
+static struct kmem_cache *exfat_inode_cachep; | |
+ | |
+static int exfat_default_codepage = CONFIG_EXFAT_DEFAULT_CODEPAGE; | |
+static char exfat_default_iocharset[] = CONFIG_EXFAT_DEFAULT_IOCHARSET; | |
+ | |
+extern struct timezone sys_tz; | |
+ | |
+#define CHECK_ERR(x) BUG_ON(x) | |
+#define ELAPSED_TIME 0 | |
+ | |
+#if (ELAPSED_TIME == 1) | |
+#include <linux/time.h> | |
+ | |
+static UINT32 __t1, __t2; | |
+static UINT32 get_current_msec(void) | |
+{ | |
+ struct timeval tm; | |
+ do_gettimeofday(&tm); | |
+ return((UINT32)(tm.tv_sec*1000000 + tm.tv_usec)); | |
+} | |
+#define TIME_START() do {__t1 = get_current_msec();} while (0) | |
+#define TIME_END() do {__t2 = get_current_msec();} while (0) | |
+#define PRINT_TIME(n) do {printk("[EXFAT] Elapsed time %d = %d (usec)\n", n, (__t2 - __t1));} while (0) | |
+#else | |
+#define TIME_START() | |
+#define TIME_END() | |
+#define PRINT_TIME(n) | |
+#endif | |
+ | |
+#define UNIX_SECS_1980 315532800L | |
+ | |
+#if BITS_PER_LONG == 64 | |
+#define UNIX_SECS_2108 4354819200L | |
+#endif | |
+/* days between 1.1.70 and 1.1.80 (2 leap days) */ | |
+#define DAYS_DELTA_DECADE (365 * 10 + 2) | |
+/* 120 (2100 - 1980) isn't leap year */ | |
+#define NO_LEAP_YEAR_2100 (120) | |
+#define IS_LEAP_YEAR(y) (!((y) & 0x3) && (y) != NO_LEAP_YEAR_2100) | |
+ | |
+#define SECS_PER_MIN (60) | |
+#define SECS_PER_HOUR (60 * SECS_PER_MIN) | |
+#define SECS_PER_DAY (24 * SECS_PER_HOUR) | |
+ | |
+#define MAKE_LEAP_YEAR(leap_year, year) \ | |
+ do { \ | |
+ if (unlikely(year > NO_LEAP_YEAR_2100)) \ | |
+ leap_year = ((year + 3) / 4) - 1; \ | |
+ else \ | |
+ leap_year = ((year + 3) / 4); \ | |
+ } while(0) | |
+ | |
+/* Linear day numbers of the respective 1sts in non-leap years. */ | |
+static time_t accum_days_in_year[] = { | |
+ /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ | |
+ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, | |
+}; | |
+ | |
+static void _exfat_truncate(struct inode *inode, loff_t old_size); | |
+ | |
+/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ | |
+void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec *ts, | |
+ DATE_TIME_T *tp) | |
+{ | |
+ time_t year = tp->Year; | |
+ time_t ld; | |
+ | |
+ MAKE_LEAP_YEAR(ld, year); | |
+ | |
+ if (IS_LEAP_YEAR(year) && (tp->Month) > 2) | |
+ ld++; | |
+ | |
+ ts->tv_sec = tp->Second + tp->Minute * SECS_PER_MIN | |
+ + tp->Hour * SECS_PER_HOUR | |
+ + (year * 365 + ld + accum_days_in_year[(tp->Month)] + (tp->Day - 1) + DAYS_DELTA_DECADE) * SECS_PER_DAY | |
+ + sys_tz.tz_minuteswest * SECS_PER_MIN; | |
+ ts->tv_nsec = 0; | |
+} | |
+ | |
+/* Convert linear UNIX date to a FAT time/date pair. */ | |
+void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec *ts, | |
+ DATE_TIME_T *tp) | |
+{ | |
+ time_t second = ts->tv_sec; | |
+ time_t day, month, year; | |
+ time_t ld; | |
+ | |
+ second -= sys_tz.tz_minuteswest * SECS_PER_MIN; | |
+ | |
+ /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ | |
+ if (second < UNIX_SECS_1980) { | |
+ tp->Second = 0; | |
+ tp->Minute = 0; | |
+ tp->Hour = 0; | |
+ tp->Day = 1; | |
+ tp->Month = 1; | |
+ tp->Year = 0; | |
+ return; | |
+ } | |
+#if (BITS_PER_LONG == 64) | |
+ if (second >= UNIX_SECS_2108) { | |
+ tp->Second = 59; | |
+ tp->Minute = 59; | |
+ tp->Hour = 23; | |
+ tp->Day = 31; | |
+ tp->Month = 12; | |
+ tp->Year = 127; | |
+ return; | |
+ } | |
+#endif | |
+ day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; | |
+ year = day / 365; | |
+ MAKE_LEAP_YEAR(ld, year); | |
+ if (year * 365 + ld > day) | |
+ year--; | |
+ | |
+ MAKE_LEAP_YEAR(ld, year); | |
+ day -= year * 365 + ld; | |
+ | |
+ if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { | |
+ month = 2; | |
+ } else { | |
+ if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) | |
+ day--; | |
+ for (month = 1; month < 12; month++) { | |
+ if (accum_days_in_year[month + 1] > day) | |
+ break; | |
+ } | |
+ } | |
+ day -= accum_days_in_year[month]; | |
+ | |
+ tp->Second = second % SECS_PER_MIN; | |
+ tp->Minute = (second / SECS_PER_MIN) % 60; | |
+ tp->Hour = (second / SECS_PER_HOUR) % 24; | |
+ tp->Day = day + 1; | |
+ tp->Month = month; | |
+ tp->Year = year; | |
+} | |
+ | |
+static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos); | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) | |
+static int exfat_generic_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); | |
+#else | |
+static long exfat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); | |
+#endif | |
+static int exfat_sync_inode(struct inode *inode); | |
+static struct inode *exfat_build_inode(struct super_block *sb, FILE_ID_T *fid, loff_t i_pos); | |
+static void exfat_detach(struct inode *inode); | |
+static void exfat_attach(struct inode *inode, loff_t i_pos); | |
+static inline unsigned long exfat_hash(loff_t i_pos); | |
+static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc); | |
+static void exfat_write_super(struct super_block *sb); | |
+ | |
+static void __lock_super(struct super_block *sb) | |
+{ | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) | |
+ lock_super(sb); | |
+#else | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ mutex_lock(&sbi->s_lock); | |
+#endif | |
+} | |
+ | |
+static void __unlock_super(struct super_block *sb) | |
+{ | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) | |
+ unlock_super(sb); | |
+#else | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ mutex_unlock(&sbi->s_lock); | |
+#endif | |
+} | |
+ | |
+static int __is_sb_dirty(struct super_block *sb) | |
+{ | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) | |
+ return sb->s_dirty; | |
+#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_dirty = 0; | |
+#else | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ sbi->s_dirt = 0; | |
+#endif | |
+} | |
+ | |
+/*======================================================================*/ | |
+/* 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(file, 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 EXFAT_CONFIG_KERNEL_DEBUG | |
+#if !(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) | |
+ struct inode *inode = filp->f_dentry->d_inode; | |
+#endif | |
+ unsigned int flags; | |
+#endif /* EXFAT_CONFIG_KERNEL_DEBUG */ | |
+ | |
+ switch (cmd) { | |
+ case EXFAT_IOCTL_GET_VOLUME_ID: | |
+ return exfat_ioctl_volume_id(inode); | |
+#if EXFAT_CONFIG_KERNEL_DEBUG | |
+ case EXFAT_IOC_GET_DEBUGFLAGS: { | |
+ struct super_block *sb = inode->i_sb; | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ | |
+ flags = sbi->debug_flags; | |
+ return put_user(flags, (int __user *)arg); | |
+ } | |
+ case EXFAT_IOC_SET_DEBUGFLAGS: { | |
+ struct super_block *sb = inode->i_sb; | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ | |
+ if (!capable(CAP_SYS_ADMIN)) | |
+ return -EPERM; | |
+ | |
+ if (get_user(flags, (int __user *) arg)) | |
+ return -EFAULT; | |
+ | |
+ __lock_super(sb); | |
+ sbi->debug_flags = flags; | |
+ __unlock_super(sb); | |
+ | |
+ return 0; | |
+ } | |
+#endif /* EXFAT_CONFIG_KERNEL_DEBUG */ | |
+ default: | |
+ return -ENOTTY; /* Inappropriate ioctl for device */ | |
+ } | |
+} | |
+ | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) | |
+static int exfat_file_fsync(struct file *filp, int datasync) | |
+{ | |
+ struct inode *inode = filp->f_mapping->host; | |
+ struct super_block *sb = inode->i_sb; | |
+ int res, err; | |
+ | |
+ res = generic_file_fsync(filp, datasync); | |
+ err = FsSyncVol(sb, 1); | |
+ | |
+ return res ? res : err; | |
+} | |
+#endif | |
+ | |
+const struct file_operations exfat_dir_operations = { | |
+ .llseek = generic_file_llseek, | |
+ .read = generic_read_dir, | |
+#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,4,00) | |
+static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, | |
+ struct nameidata *nd) | |
+#else | |
+static int exfat_create(struct inode *dir, struct dentry *dentry, int mode, | |
+ struct nameidata *nd) | |
+#endif | |
+{ | |
+ struct super_block *sb = dir->i_sb; | |
+ struct inode *inode; | |
+ struct timespec ts; | |
+ FILE_ID_T fid; | |
+ loff_t i_pos; | |
+ int err; | |
+ | |
+ __lock_super(sb); | |
+ | |
+ PRINTK("exfat_create entered\n"); | |
+ | |
+ ts = CURRENT_TIME_SEC; | |
+ | |
+ err = FsCreateFile(dir, (UINT8 *) dentry->d_name.name, FM_REGULAR, &fid); | |
+ if (err) { | |
+ if (err == FFS_INVALIDPATH) | |
+ err = -EINVAL; | |
+ else if (err == FFS_FILEEXIST) | |
+ err = -EEXIST; | |
+ else if (err == FFS_FULL) | |
+ err = -ENOSPC; | |
+ else if (err == FFS_NAMETOOLONG) | |
+ err = -ENAMETOOLONG; | |
+ else | |
+ err = -EIO; | |
+ goto out; | |
+ } | |
+ dir->i_version++; | |
+ dir->i_ctime = dir->i_mtime = dir->i_atime = ts; | |
+ if (IS_DIRSYNC(dir)) | |
+ (void) exfat_sync_inode(dir); | |
+ else | |
+ mark_inode_dirty(dir); | |
+ | |
+ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); | |
+ | |
+ inode = exfat_build_inode(sb, &fid, i_pos); | |
+ if (IS_ERR(inode)) { | |
+ err = PTR_ERR(inode); | |
+ goto out; | |
+ } | |
+ inode->i_version++; | |
+ inode->i_mtime = inode->i_atime = inode->i_ctime = ts; | |
+ /* timestamp is already written, so mark_inode_dirty() is unnecessary. */ | |
+ | |
+ dentry->d_time = dentry->d_parent->d_inode->i_version; | |
+ d_instantiate(dentry, inode); | |
+ | |
+out: | |
+ __unlock_super(sb); | |
+ PRINTK("exfat_create exited\n"); | |
+ return err; | |
+} | |
+ | |
+static int exfat_find(struct inode *dir, struct qstr *qname, | |
+ FILE_ID_T *fid) | |
+{ | |
+ int err; | |
+ | |
+ if (qname->len == 0) | |
+ return -ENOENT; | |
+ | |
+ err = FsLookupFile(dir, (UINT8 *) qname->name, fid); | |
+ if (err) | |
+ return -ENOENT; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int exfat_d_anon_disconn(struct dentry *dentry) | |
+{ | |
+ return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED); | |
+} | |
+ | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) | |
+static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, | |
+ unsigned int flags) | |
+#else | |
+static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, | |
+ struct nameidata *nd) | |
+#endif | |
+{ | |
+ struct super_block *sb = dir->i_sb; | |
+ struct inode *inode; | |
+ struct dentry *alias; | |
+ int err; | |
+ FILE_ID_T fid; | |
+ loff_t i_pos; | |
+ UINT64 ret; | |
+ mode_t i_mode; | |
+ | |
+ __lock_super(sb); | |
+ PRINTK("exfat_lookup entered\n"); | |
+ err = exfat_find(dir, &dentry->d_name, &fid); | |
+ if (err) { | |
+ if (err == -ENOENT) { | |
+ inode = NULL; | |
+ goto out; | |
+ } | |
+ goto error; | |
+ } | |
+ | |
+ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); | |
+ inode = exfat_build_inode(sb, &fid, i_pos); | |
+ if (IS_ERR(inode)) { | |
+ err = PTR_ERR(inode); | |
+ goto error; | |
+ } | |
+ | |
+ i_mode = inode->i_mode; | |
+ if (S_ISLNK(i_mode)) { | |
+ EXFAT_I(inode)->target = MALLOC(i_size_read(inode)+1); | |
+ if (!EXFAT_I(inode)->target) { | |
+ err = -ENOMEM; | |
+ goto error; | |
+ } | |
+ FsReadFile(dir, &fid, EXFAT_I(inode)->target, i_size_read(inode), &ret); | |
+ *(EXFAT_I(inode)->target + i_size_read(inode)) = '\0'; | |
+ } | |
+ | |
+ alias = d_find_alias(inode); | |
+ if (alias && !exfat_d_anon_disconn(alias)) { | |
+ CHECK_ERR(d_unhashed(alias)); | |
+ if (!S_ISDIR(i_mode)) | |
+ d_move(alias, dentry); | |
+ iput(inode); | |
+ __unlock_super(sb); | |
+ PRINTK("exfat_lookup exited 1\n"); | |
+ return alias; | |
+ } else { | |
+ dput(alias); | |
+ } | |
+out: | |
+ __unlock_super(sb); | |
+ dentry->d_time = dentry->d_parent->d_inode->i_version; | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) | |
+ dentry->d_op = sb->s_root->d_op; | |
+ dentry = d_splice_alias(inode, dentry); | |
+ if (dentry) { | |
+ dentry->d_op = sb->s_root->d_op; | |
+ dentry->d_time = dentry->d_parent->d_inode->i_version; | |
+ } | |
+#else | |
+ dentry = d_splice_alias(inode, dentry); | |
+ if (dentry) | |
+ dentry->d_time = dentry->d_parent->d_inode->i_version; | |
+#endif | |
+ PRINTK("exfat_lookup exited 2\n"); | |
+ return dentry; | |
+ | |
+error: | |
+ __unlock_super(sb); | |
+ PRINTK("exfat_lookup exited 3\n"); | |
+ return ERR_PTR(err); | |
+} | |
+ | |
+static int exfat_unlink(struct inode *dir, struct dentry *dentry) | |
+{ | |
+ struct inode *inode = dentry->d_inode; | |
+ struct super_block *sb = dir->i_sb; | |
+ struct timespec ts; | |
+ int err; | |
+ | |
+ __lock_super(sb); | |
+ | |
+ PRINTK("exfat_unlink entered\n"); | |
+ | |
+ ts = CURRENT_TIME_SEC; | |
+ | |
+ EXFAT_I(inode)->fid.size = i_size_read(inode); | |
+ | |
+ err = FsRemoveFile(dir, &(EXFAT_I(inode)->fid)); | |
+ if (err) { | |
+ if (err == FFS_PERMISSIONERR) | |
+ err = -EPERM; | |
+ else | |
+ err = -EIO; | |
+ goto out; | |
+ } | |
+ dir->i_version++; | |
+ dir->i_mtime = dir->i_atime = ts; | |
+ if (IS_DIRSYNC(dir)) | |
+ (void) exfat_sync_inode(dir); | |
+ else | |
+ mark_inode_dirty(dir); | |
+ | |
+ clear_nlink(inode); | |
+ inode->i_mtime = inode->i_atime = ts; | |
+ exfat_detach(inode); | |
+ remove_inode_hash(inode); | |
+ | |
+out: | |
+ __unlock_super(sb); | |
+ PRINTK("exfat_unlink exited\n"); | |
+ return err; | |
+} | |
+ | |
+static int exfat_symlink(struct inode *dir, struct dentry *dentry, const char *target) | |
+{ | |
+ struct super_block *sb = dir->i_sb; | |
+ struct inode *inode; | |
+ struct timespec ts; | |
+ FILE_ID_T fid; | |
+ loff_t i_pos; | |
+ int err; | |
+ UINT64 len = (UINT64) strlen(target); | |
+ UINT64 ret; | |
+ | |
+ __lock_super(sb); | |
+ | |
+ PRINTK("exfat_symlink entered\n"); | |
+ | |
+ ts = CURRENT_TIME_SEC; | |
+ | |
+ err = FsCreateFile(dir, (UINT8 *) dentry->d_name.name, FM_SYMLINK, &fid); | |
+ if (err) { | |
+ if (err == FFS_INVALIDPATH) | |
+ err = -EINVAL; | |
+ else if (err == FFS_FILEEXIST) | |
+ err = -EEXIST; | |
+ else if (err == FFS_FULL) | |
+ err = -ENOSPC; | |
+ else | |
+ err = -EIO; | |
+ goto out; | |
+ } | |
+ | |
+ err = FsWriteFile(dir, &fid, (char *) target, len, &ret); | |
+ | |
+ if (err) { | |
+ FsRemoveFile(dir, &fid); | |
+ | |
+ if (err == FFS_FULL) | |
+ err = -ENOSPC; | |
+ else | |
+ err = -EIO; | |
+ goto out; | |
+ } | |
+ | |
+ dir->i_version++; | |
+ dir->i_ctime = dir->i_mtime = dir->i_atime = ts; | |
+ if (IS_DIRSYNC(dir)) | |
+ (void) exfat_sync_inode(dir); | |
+ else | |
+ mark_inode_dirty(dir); | |
+ | |
+ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); | |
+ | |
+ inode = exfat_build_inode(sb, &fid, i_pos); | |
+ if (IS_ERR(inode)) { | |
+ err = PTR_ERR(inode); | |
+ goto out; | |
+ } | |
+ inode->i_version++; | |
+ inode->i_mtime = inode->i_atime = inode->i_ctime = ts; | |
+ /* timestamp is already written, so mark_inode_dirty() is unneeded. */ | |
+ | |
+ EXFAT_I(inode)->target = MALLOC(len+1); | |
+ if (!EXFAT_I(inode)->target) { | |
+ err = -ENOMEM; | |
+ goto out; | |
+ } | |
+ MEMCPY(EXFAT_I(inode)->target, target, len+1); | |
+ | |
+ dentry->d_time = dentry->d_parent->d_inode->i_version; | |
+ d_instantiate(dentry, inode); | |
+ | |
+out: | |
+ __unlock_super(sb); | |
+ PRINTK("exfat_symlink exited\n"); | |
+ return err; | |
+} | |
+ | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) | |
+static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) | |
+#else | |
+static int exfat_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |
+#endif | |
+{ | |
+ struct super_block *sb = dir->i_sb; | |
+ struct inode *inode; | |
+ struct timespec ts; | |
+ FILE_ID_T fid; | |
+ loff_t i_pos; | |
+ int err; | |
+ | |
+ __lock_super(sb); | |
+ | |
+ PRINTK("exfat_mkdir entered\n"); | |
+ | |
+ ts = CURRENT_TIME_SEC; | |
+ | |
+ err = FsCreateDir(dir, (UINT8 *) dentry->d_name.name, &fid); | |
+ if (err) { | |
+ if (err == FFS_INVALIDPATH) | |
+ err = -EINVAL; | |
+ else if (err == FFS_FILEEXIST) | |
+ err = -EEXIST; | |
+ else if (err == FFS_FULL) | |
+ err = -ENOSPC; | |
+ else if (err == FFS_NAMETOOLONG) | |
+ err = -ENAMETOOLONG; | |
+ else | |
+ err = -EIO; | |
+ goto out; | |
+ } | |
+ dir->i_version++; | |
+ dir->i_ctime = dir->i_mtime = dir->i_atime = ts; | |
+ if (IS_DIRSYNC(dir)) | |
+ (void) exfat_sync_inode(dir); | |
+ else | |
+ mark_inode_dirty(dir); | |
+ inc_nlink(dir); | |
+ | |
+ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); | |
+ | |
+ inode = exfat_build_inode(sb, &fid, i_pos); | |
+ if (IS_ERR(inode)) { | |
+ err = PTR_ERR(inode); | |
+ goto out; | |
+ } | |
+ inode->i_version++; | |
+ inode->i_mtime = inode->i_atime = inode->i_ctime = ts; | |
+ /* timestamp is already written, so mark_inode_dirty() is unneeded. */ | |
+ | |
+ dentry->d_time = dentry->d_parent->d_inode->i_version; | |
+ d_instantiate(dentry, inode); | |
+ | |
+out: | |
+ __unlock_super(sb); | |
+ PRINTK("exfat_mkdir exited\n"); | |
+ return err; | |
+} | |
+ | |
+static int exfat_rmdir(struct inode *dir, struct dentry *dentry) | |
+{ | |
+ struct inode *inode = dentry->d_inode; | |
+ struct super_block *sb = dir->i_sb; | |
+ struct timespec ts; | |
+ int err; | |
+ | |
+ __lock_super(sb); | |
+ | |
+ PRINTK("exfat_rmdir entered\n"); | |
+ | |
+ ts = CURRENT_TIME_SEC; | |
+ | |
+ EXFAT_I(inode)->fid.size = i_size_read(inode); | |
+ | |
+ err = FsRemoveDir(dir, &(EXFAT_I(inode)->fid)); | |
+ if (err) { | |
+ if (err == FFS_INVALIDPATH) | |
+ err = -EINVAL; | |
+ else if (err == FFS_FILEEXIST) | |
+ err = -ENOTEMPTY; | |
+ else if (err == FFS_NOTFOUND) | |
+ err = -ENOENT; | |
+ else if (err == FFS_DIRBUSY) | |
+ err = -EBUSY; | |
+ else | |
+ err = -EIO; | |
+ goto out; | |
+ } | |
+ dir->i_version++; | |
+ dir->i_mtime = dir->i_atime = ts; | |
+ if (IS_DIRSYNC(dir)) | |
+ (void) exfat_sync_inode(dir); | |
+ else | |
+ mark_inode_dirty(dir); | |
+ drop_nlink(dir); | |
+ | |
+ clear_nlink(inode); | |
+ inode->i_mtime = inode->i_atime = ts; | |
+ exfat_detach(inode); | |
+ remove_inode_hash(inode); | |
+ | |
+out: | |
+ __unlock_super(sb); | |
+ PRINTK("exfat_rmdir exited\n"); | |
+ return err; | |
+} | |
+ | |
+static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, | |
+ struct inode *new_dir, struct dentry *new_dentry) | |
+{ | |
+ struct inode *old_inode, *new_inode; | |
+ struct super_block *sb = old_dir->i_sb; | |
+ struct timespec ts; | |
+ loff_t i_pos; | |
+ int err; | |
+ | |
+ __lock_super(sb); | |
+ | |
+ PRINTK("exfat_rename entered\n"); | |
+ | |
+ old_inode = old_dentry->d_inode; | |
+ new_inode = new_dentry->d_inode; | |
+ | |
+ ts = CURRENT_TIME_SEC; | |
+ | |
+ EXFAT_I(old_inode)->fid.size = i_size_read(old_inode); | |
+ | |
+ err = FsMoveFile(old_dir, &(EXFAT_I(old_inode)->fid), new_dir, new_dentry); | |
+ if (err) { | |
+ if (err == FFS_PERMISSIONERR) | |
+ err = -EPERM; | |
+ else if (err == FFS_INVALIDPATH) | |
+ err = -EINVAL; | |
+ else if (err == FFS_FILEEXIST) | |
+ err = -EEXIST; | |
+ else if (err == FFS_NOTFOUND) | |
+ err = -ENOENT; | |
+ else if (err == FFS_FULL) | |
+ err = -ENOSPC; | |
+ else | |
+ err = -EIO; | |
+ goto out; | |
+ } | |
+ new_dir->i_version++; | |
+ new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime = ts; | |
+ if (IS_DIRSYNC(new_dir)) | |
+ (void) exfat_sync_inode(new_dir); | |
+ else | |
+ mark_inode_dirty(new_dir); | |
+ | |
+ i_pos = ((loff_t) EXFAT_I(old_inode)->fid.dir.dir << 32) | | |
+ (EXFAT_I(old_inode)->fid.entry & 0xffffffff); | |
+ | |
+ exfat_detach(old_inode); | |
+ exfat_attach(old_inode, i_pos); | |
+ if (IS_DIRSYNC(new_dir)) | |
+ (void) exfat_sync_inode(old_inode); | |
+ else | |
+ mark_inode_dirty(old_inode); | |
+ | |
+ if ((S_ISDIR(old_inode->i_mode)) && (old_dir != new_dir)) { | |
+ drop_nlink(old_dir); | |
+ if (!new_inode) inc_nlink(new_dir); | |
+ } | |
+ | |
+ old_dir->i_version++; | |
+ old_dir->i_ctime = old_dir->i_mtime = ts; | |
+ if (IS_DIRSYNC(old_dir)) | |
+ (void) exfat_sync_inode(old_dir); | |
+ else | |
+ mark_inode_dirty(old_dir); | |
+ | |
+ if (new_inode) { | |
+ exfat_detach(new_inode); | |
+ drop_nlink(new_inode); | |
+ if (S_ISDIR(new_inode->i_mode)) | |
+ drop_nlink(new_inode); | |
+ new_inode->i_ctime = ts; | |
+ } | |
+ | |
+out: | |
+ __unlock_super(sb); | |
+ PRINTK("exfat_rename exited\n"); | |
+ return err; | |
+} | |
+ | |
+static int exfat_cont_expand(struct inode *inode, loff_t size) | |
+{ | |
+ struct address_space *mapping = inode->i_mapping; | |
+ loff_t start = i_size_read(inode), count = size - i_size_read(inode); | |
+ int err, err2; | |
+ | |
+ if ((err = generic_cont_expand_simple(inode, size)) != 0) | |
+ return err; | |
+ | |
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; | |
+ mark_inode_dirty(inode); | |
+ | |
+ if (IS_SYNC(inode)) { | |
+ err = filemap_fdatawrite_range(mapping, start, start + count - 1); | |
+ err2 = sync_mapping_buffers(mapping); | |
+ err = (err)?(err):(err2); | |
+ err2 = write_inode_now(inode, 1); | |
+ err = (err)?(err):(err2); | |
+ if (!err) { | |
+ err = filemap_fdatawait_range(mapping, start, start + count - 1); | |
+ } | |
+ } | |
+ return err; | |
+} | |
+ | |
+static int exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode) | |
+{ | |
+ mode_t allow_utime = sbi->options.allow_utime; | |
+ | |
+#if 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; | |
+ loff_t old_size; | |
+ | |
+ PRINTK("exfat_setattr entered\n"); | |
+ | |
+ if ((attr->ia_valid & ATTR_SIZE) | |
+ && (attr->ia_size > i_size_read(inode))) { | |
+ error = exfat_cont_expand(inode, attr->ia_size); | |
+ if (error || attr->ia_valid == ATTR_SIZE) | |
+ return error; | |
+ attr->ia_valid &= ~ATTR_SIZE; | |
+ } | |
+ | |
+ ia_valid = attr->ia_valid; | |
+ | |
+ if ((ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) | |
+ && exfat_allow_set_time(sbi, inode)) { | |
+ attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET); | |
+ } | |
+ | |
+ error = inode_change_ok(inode, attr); | |
+ attr->ia_valid = ia_valid; | |
+ if (error) { | |
+ return error; | |
+ } | |
+ | |
+ if (((attr->ia_valid & ATTR_UID) && | |
+#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 | |
+ | |
+ PRINTK("exfat_setattr exited\n"); | |
+ return error; | |
+} | |
+ | |
+static int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) | |
+{ | |
+ struct inode *inode = dentry->d_inode; | |
+ | |
+ PRINTK("exfat_getattr entered\n"); | |
+ | |
+ generic_fillattr(inode, stat); | |
+ stat->blksize = EXFAT_SB(inode->i_sb)->fs_info.cluster_size; | |
+ | |
+ PRINTK("exfat_getattr exited\n"); | |
+ return 0; | |
+} | |
+ | |
+const struct inode_operations exfat_dir_inode_operations = { | |
+ .create = exfat_create, | |
+ .lookup = exfat_lookup, | |
+ .unlink = exfat_unlink, | |
+ .symlink = exfat_symlink, | |
+ .mkdir = exfat_mkdir, | |
+ .rmdir = exfat_rmdir, | |
+ .rename = exfat_rename, | |
+ .setattr = exfat_setattr, | |
+ .getattr = exfat_getattr, | |
+}; | |
+ | |
+/*======================================================================*/ | |
+/* File Operations */ | |
+/*======================================================================*/ | |
+ | |
+static void *exfat_follow_link(struct dentry *dentry, struct nameidata *nd) | |
+{ | |
+ struct exfat_inode_info *ei = EXFAT_I(dentry->d_inode); | |
+ nd_set_link(nd, (char *)(ei->target)); | |
+ return NULL; | |
+} | |
+ | |
+const struct inode_operations exfat_symlink_inode_operations = { | |
+ .readlink = generic_readlink, | |
+ .follow_link = exfat_follow_link, | |
+}; | |
+ | |
+static int exfat_file_release(struct inode *inode, struct file *filp) | |
+{ | |
+ struct super_block *sb = inode->i_sb; | |
+ | |
+ EXFAT_I(inode)->fid.size = i_size_read(inode); | |
+ FsSyncVol(sb, 0); | |
+ return 0; | |
+} | |
+ | |
+const struct file_operations exfat_file_operations = { | |
+ .llseek = generic_file_llseek, | |
+ .read = do_sync_read, | |
+ .write = do_sync_write, | |
+ .aio_read = generic_file_aio_read, | |
+ .aio_write = generic_file_aio_write, | |
+ .mmap = generic_file_mmap, | |
+ .release = exfat_file_release, | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) | |
+ .ioctl = exfat_generic_ioctl, | |
+ .fsync = exfat_file_fsync, | |
+#else | |
+ .unlocked_ioctl = exfat_generic_ioctl, | |
+ .fsync = generic_file_fsync, | |
+#endif | |
+ .splice_read = generic_file_splice_read, | |
+}; | |
+ | |
+static void _exfat_truncate(struct inode *inode, loff_t old_size) | |
+{ | |
+ struct super_block *sb = inode->i_sb; | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ FS_INFO_T *p_fs = &(sbi->fs_info); | |
+ int err; | |
+ | |
+ __lock_super(sb); | |
+ | |
+ /* | |
+ * This protects against truncating a file bigger than it was then | |
+ * trying to write into the hole. | |
+ */ | |
+ if (EXFAT_I(inode)->mmu_private > i_size_read(inode)) | |
+ EXFAT_I(inode)->mmu_private = i_size_read(inode); | |
+ | |
+ if (EXFAT_I(inode)->fid.start_clu == 0) goto out; | |
+ | |
+ err = FsTruncateFile(inode, old_size, i_size_read(inode)); | |
+ if (err) goto out; | |
+ | |
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; | |
+ if (IS_DIRSYNC(inode)) | |
+ (void) exfat_sync_inode(inode); | |
+ else | |
+ mark_inode_dirty(inode); | |
+ | |
+ inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) | |
+ & ~((loff_t)p_fs->cluster_size - 1)) >> 9; | |
+out: | |
+ __unlock_super(sb); | |
+} | |
+ | |
+#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)) { | |
+ truncate_pagecache(inode, to, i_size_read(inode)); | |
+ EXFAT_I(inode)->fid.size = i_size_read(inode); | |
+ _exfat_truncate(inode, i_size_read(inode)); | |
+ } | |
+} | |
+#endif | |
+ | |
+ | |
+static int exfat_write_begin(struct file *file, struct address_space *mapping, | |
+ loff_t pos, unsigned len, unsigned flags, | |
+ struct page **pagep, void **fsdata) | |
+{ | |
+ int ret; | |
+ *pagep = NULL; | |
+ ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, | |
+ exfat_get_block, | |
+ &EXFAT_I(mapping->host)->mmu_private); | |
+ | |
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) | |
+ if (ret < 0) | |
+ exfat_write_failed(mapping, pos+len); | |
+#endif | |
+ return ret; | |
+} | |
+ | |
+static int exfat_write_end(struct file *file, struct address_space *mapping, | |
+ loff_t pos, unsigned len, unsigned copied, | |
+ struct page *pagep, void *fsdata) | |
+{ | |
+ struct inode *inode = mapping->host; | |
+ FILE_ID_T *fid = &(EXFAT_I(inode)->fid); | |
+ int err; | |
+ | |
+ err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata); | |
+ | |
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) | |
+ if (err < len) | |
+ exfat_write_failed(mapping, pos+len); | |
+#endif | |
+ | |
+ if (!(err < 0) && !(fid->attr & ATTR_ARCHIVE)) { | |
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; | |
+ fid->attr |= ATTR_ARCHIVE; | |
+ mark_inode_dirty(inode); | |
+ } | |
+ return err; | |
+} | |
+ | |
+static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, | |
+ const struct iovec *iov, | |
+ loff_t offset, unsigned long nr_segs) | |
+{ | |
+ struct inode *inode = iocb->ki_filp->f_mapping->host; | |
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) | |
+ struct address_space *mapping = iocb->ki_filp->f_mapping; | |
+#endif | |
+ ssize_t ret; | |
+ | |
+ if (rw == WRITE) { | |
+ if (EXFAT_I(inode)->mmu_private < (offset + iov_length(iov, nr_segs))) | |
+ return 0; | |
+ } | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) | |
+ ret = blockdev_direct_IO(rw, iocb, inode, iov, | |
+ offset, nr_segs, exfat_get_block); | |
+#else | |
+ ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, | |
+ offset, nr_segs, exfat_get_block, NULL); | |
+#endif | |
+ | |
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) | |
+ if ((ret < 0) && (rw & WRITE)) | |
+ exfat_write_failed(mapping, offset+iov_length(iov, nr_segs)); | |
+#endif | |
+ return ret; | |
+} | |
+ | |
+static sector_t _exfat_bmap(struct address_space *mapping, sector_t block) | |
+{ | |
+ sector_t blocknr; | |
+ | |
+ /* exfat_get_cluster() assumes the requested blocknr isn't truncated. */ | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) | |
+ down_read(&EXFAT_I(mapping->host)->truncate_lock); | |
+ blocknr = generic_block_bmap(mapping, block, exfat_get_block); | |
+ up_read(&EXFAT_I(mapping->host)->truncate_lock); | |
+#else | |
+ down_read(&EXFAT_I(mapping->host)->i_alloc_sem); | |
+ blocknr = generic_block_bmap(mapping, block, exfat_get_block); | |
+ up_read(&EXFAT_I(mapping->host)->i_alloc_sem); | |
+#endif | |
+ | |
+ return blocknr; | |
+} | |
+ | |
+const struct address_space_operations exfat_aops = { | |
+ .readpage = exfat_readpage, | |
+ .readpages = exfat_readpages, | |
+ .writepage = exfat_writepage, | |
+ .writepages = exfat_writepages, | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) | |
+ .sync_page = block_sync_page, | |
+#endif | |
+ .write_begin = exfat_write_begin, | |
+ .write_end = exfat_write_end, | |
+ .direct_IO = exfat_direct_IO, | |
+ .bmap = _exfat_bmap | |
+}; | |
+ | |
+/*======================================================================*/ | |
+/* Super Operations */ | |
+/*======================================================================*/ | |
+ | |
+static inline unsigned long exfat_hash(loff_t i_pos) | |
+{ | |
+ return hash_32(i_pos, EXFAT_HASH_BITS); | |
+} | |
+ | |
+static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos) { | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ struct exfat_inode_info *info; | |
+ struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); | |
+ struct inode *inode = NULL; | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) | |
+ struct hlist_node *node; | |
+ | |
+ spin_lock(&sbi->inode_hash_lock); | |
+ hlist_for_each_entry(info, node, head, i_hash_fat) { | |
+#else | |
+ spin_lock(&sbi->inode_hash_lock); | |
+ hlist_for_each_entry(info, head, i_hash_fat) { | |
+#endif | |
+ CHECK_ERR(info->vfs_inode.i_sb != sb); | |
+ | |
+ if (i_pos != info->i_pos) | |
+ continue; | |
+ inode = igrab(&info->vfs_inode); | |
+ if (inode) | |
+ break; | |
+ } | |
+ spin_unlock(&sbi->inode_hash_lock); | |
+ return inode; | |
+} | |
+ | |
+static void exfat_attach(struct inode *inode, loff_t i_pos) | |
+{ | |
+ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); | |
+ struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); | |
+ | |
+ spin_lock(&sbi->inode_hash_lock); | |
+ EXFAT_I(inode)->i_pos = i_pos; | |
+ hlist_add_head(&EXFAT_I(inode)->i_hash_fat, head); | |
+ spin_unlock(&sbi->inode_hash_lock); | |
+} | |
+ | |
+static void exfat_detach(struct inode *inode) | |
+{ | |
+ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); | |
+ | |
+ spin_lock(&sbi->inode_hash_lock); | |
+ hlist_del_init(&EXFAT_I(inode)->i_hash_fat); | |
+ EXFAT_I(inode)->i_pos = 0; | |
+ spin_unlock(&sbi->inode_hash_lock); | |
+} | |
+ | |
+/* doesn't deal with root inode */ | |
+static int exfat_fill_inode(struct inode *inode, FILE_ID_T *fid) | |
+{ | |
+ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); | |
+ FS_INFO_T *p_fs = &(sbi->fs_info); | |
+ DIR_ENTRY_T info; | |
+ | |
+ memcpy(&(EXFAT_I(inode)->fid), fid, sizeof(FILE_ID_T)); | |
+ | |
+ FsReadStat(inode, &info); | |
+ | |
+ EXFAT_I(inode)->i_pos = 0; | |
+ EXFAT_I(inode)->target = NULL; | |
+ inode->i_uid = sbi->options.fs_uid; | |
+ inode->i_gid = sbi->options.fs_gid; | |
+ inode->i_version++; | |
+ inode->i_generation = get_seconds(); | |
+ | |
+ if (info.Attr & ATTR_SUBDIR) { /* directory */ | |
+ inode->i_generation &= ~1; | |
+ inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); | |
+ inode->i_op = &exfat_dir_inode_operations; | |
+ inode->i_fop = &exfat_dir_operations; | |
+ | |
+ i_size_write(inode, info.Size); | |
+ EXFAT_I(inode)->mmu_private = i_size_read(inode); | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) | |
+ set_nlink(inode,info.NumSubdirs); | |
+#else | |
+ inode->i_nlink = info.NumSubdirs; | |
+#endif | |
+ } else if (info.Attr & ATTR_SYMLINK) { /* symbolic link */ | |
+ inode->i_generation |= 1; | |
+ inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); | |
+ inode->i_op = &exfat_symlink_inode_operations; | |
+ | |
+ i_size_write(inode, info.Size); | |
+ EXFAT_I(inode)->mmu_private = i_size_read(inode); | |
+ } else { /* regular file */ | |
+ inode->i_generation |= 1; | |
+ inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); | |
+ inode->i_op = &exfat_file_inode_operations; | |
+ inode->i_fop = &exfat_file_operations; | |
+ inode->i_mapping->a_ops = &exfat_aops; | |
+ inode->i_mapping->nrpages = 0; | |
+ | |
+ i_size_write(inode, info.Size); | |
+ EXFAT_I(inode)->mmu_private = i_size_read(inode); | |
+ } | |
+ exfat_save_attr(inode, info.Attr); | |
+ | |
+ inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) | |
+ & ~((loff_t)p_fs->cluster_size - 1)) >> 9; | |
+ | |
+ exfat_time_fat2unix(sbi, &inode->i_mtime, &info.ModifyTimestamp); | |
+ exfat_time_fat2unix(sbi, &inode->i_ctime, &info.CreateTimestamp); | |
+ exfat_time_fat2unix(sbi, &inode->i_atime, &info.AccessTimestamp); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static struct inode *exfat_build_inode(struct super_block *sb, | |
+ FILE_ID_T *fid, loff_t i_pos) { | |
+ struct inode *inode; | |
+ int err; | |
+ | |
+ inode = exfat_iget(sb, i_pos); | |
+ if (inode) | |
+ goto out; | |
+ inode = new_inode(sb); | |
+ if (!inode) { | |
+ inode = ERR_PTR(-ENOMEM); | |
+ goto out; | |
+ } | |
+ inode->i_ino = iunique(sb, EXFAT_ROOT_INO); | |
+ inode->i_version = 1; | |
+ err = exfat_fill_inode(inode, fid); | |
+ if (err) { | |
+ iput(inode); | |
+ inode = ERR_PTR(err); | |
+ goto out; | |
+ } | |
+ exfat_attach(inode, i_pos); | |
+ insert_inode_hash(inode); | |
+out: | |
+ return inode; | |
+} | |
+ | |
+static int exfat_sync_inode(struct inode *inode) | |
+{ | |
+ return exfat_write_inode(inode, NULL); | |
+} | |
+ | |
+static struct inode *exfat_alloc_inode(struct super_block *sb) { | |
+ struct exfat_inode_info *ei; | |
+ | |
+ ei = kmem_cache_alloc(exfat_inode_cachep, GFP_NOFS); | |
+ if (!ei) | |
+ return NULL; | |
+ | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) | |
+ init_rwsem(&ei->truncate_lock); | |
+#endif | |
+ | |
+ return &ei->vfs_inode; | |
+} | |
+ | |
+static void exfat_destroy_inode(struct inode *inode) | |
+{ | |
+ FREE(EXFAT_I(inode)->target); | |
+ EXFAT_I(inode)->target = NULL; | |
+ | |
+ kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode)); | |
+} | |
+ | |
+static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) | |
+{ | |
+ struct super_block *sb = inode->i_sb; | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ DIR_ENTRY_T info; | |
+ | |
+ if (inode->i_ino == EXFAT_ROOT_INO) | |
+ return 0; | |
+ | |
+ info.Attr = exfat_make_attr(inode); | |
+ info.Size = i_size_read(inode); | |
+ | |
+ exfat_time_unix2fat(sbi, &inode->i_mtime, &info.ModifyTimestamp); | |
+ exfat_time_unix2fat(sbi, &inode->i_ctime, &info.CreateTimestamp); | |
+ exfat_time_unix2fat(sbi, &inode->i_atime, &info.AccessTimestamp); | |
+ | |
+ FsWriteStat(inode, &info); | |
+ | |
+ return 0; | |
+} | |
+ | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) | |
+static void exfat_delete_inode(struct inode *inode) | |
+{ | |
+ truncate_inode_pages(&inode->i_data, 0); | |
+ clear_inode(inode); | |
+} | |
+ | |
+static void exfat_clear_inode(struct inode *inode) | |
+{ | |
+ exfat_detach(inode); | |
+ remove_inode_hash(inode); | |
+} | |
+#else | |
+static void exfat_evict_inode(struct inode *inode) | |
+{ | |
+ truncate_inode_pages(&inode->i_data, 0); | |
+ | |
+ if (!inode->i_nlink) | |
+ i_size_write(inode, 0); | |
+ invalidate_inode_buffers(inode); | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,00) | |
+ end_writeback(inode); | |
+#else | |
+ clear_inode(inode); | |
+#endif | |
+ exfat_detach(inode); | |
+ | |
+ remove_inode_hash(inode); | |
+} | |
+#endif | |
+ | |
+static void exfat_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); | |
+ mutex_destroy(&sbi->s_lock); | |
+ 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 == (UINT32) ~0) { | |
+ if (FFS_MEDIAERR == FsGetVolInfo(sb, &info)) | |
+ return -EIO; | |
+ | |
+ } else { | |
+ info.FatType = p_fs->vol_type; | |
+ info.ClusterSize = p_fs->cluster_size; | |
+ info.NumClusters = p_fs->num_clusters - 2; | |
+ info.UsedClusters = p_fs->used_clusters; | |
+ info.FreeClusters = info.NumClusters - info.UsedClusters; | |
+ | |
+ if (p_fs->dev_ejected) | |
+ return -EIO; | |
+ } | |
+ | |
+ buf->f_type = sb->s_magic; | |
+ buf->f_bsize = info.ClusterSize; | |
+ buf->f_blocks = info.NumClusters; | |
+ buf->f_bfree = info.FreeClusters; | |
+ buf->f_bavail = info.FreeClusters; | |
+ buf->f_fsid.val[0] = (u32)id; | |
+ buf->f_fsid.val[1] = (u32)(id >> 32); | |
+ buf->f_namelen = 260; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int exfat_remount(struct super_block *sb, int *flags, char *data) | |
+{ | |
+ *flags |= MS_NODIRATIME; | |
+ return 0; | |
+} | |
+ | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) | |
+static int exfat_show_options(struct seq_file *m, struct dentry *root) | |
+{ | |
+ struct exfat_sb_info *sbi = EXFAT_SB(root->d_sb); | |
+#else | |
+static int exfat_show_options(struct seq_file *m, struct vfsmount *mnt) | |
+{ | |
+ struct exfat_sb_info *sbi = EXFAT_SB(mnt->mnt_sb); | |
+#endif | |
+ struct exfat_mount_options *opts = &sbi->options; | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) | |
+ if (__kuid_val(opts->fs_uid)) | |
+ seq_printf(m, ",uid=%u", __kuid_val(opts->fs_uid)); | |
+ if (__kgid_val(opts->fs_gid)) | |
+ seq_printf(m, ",gid=%u", __kgid_val(opts->fs_gid)); | |
+#else | |
+ if (opts->fs_uid != 0) | |
+ seq_printf(m, ",uid=%u", opts->fs_uid); | |
+ if (opts->fs_gid != 0) | |
+ seq_printf(m, ",gid=%u", opts->fs_gid); | |
+#endif | |
+ seq_printf(m, ",fmask=%04o", opts->fs_fmask); | |
+ seq_printf(m, ",dmask=%04o", opts->fs_dmask); | |
+ if (opts->allow_utime) | |
+ seq_printf(m, ",allow_utime=%04o", opts->allow_utime); | |
+ if (sbi->nls_disk) | |
+ seq_printf(m, ",codepage=%s", sbi->nls_disk->charset); | |
+ if (sbi->nls_io) | |
+ seq_printf(m, ",iocharset=%s", sbi->nls_io->charset); | |
+ seq_printf(m, ",namecase=%u", opts->casesensitive); | |
+ if (opts->errors == EXFAT_ERRORS_CONT) | |
+ seq_puts(m, ",errors=continue"); | |
+ else if (opts->errors == EXFAT_ERRORS_PANIC) | |
+ seq_puts(m, ",errors=panic"); | |
+ else | |
+ seq_puts(m, ",errors=remount-ro"); | |
+#if EXFAT_CONFIG_DISCARD | |
+ if (opts->discard) | |
+ seq_printf(m, ",discard"); | |
+#endif | |
+ return 0; | |
+} | |
+ | |
+const struct super_operations exfat_sops = { | |
+ .alloc_inode = exfat_alloc_inode, | |
+ .destroy_inode = exfat_destroy_inode, | |
+ .write_inode = exfat_write_inode, | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) | |
+ .delete_inode = exfat_delete_inode, | |
+ .clear_inode = exfat_clear_inode, | |
+#else | |
+ .evict_inode = exfat_evict_inode, | |
+#endif | |
+ .put_super = exfat_put_super, | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) | |
+ .write_super = exfat_write_super, | |
+#endif | |
+ .sync_fs = exfat_sync_fs, | |
+ .statfs = exfat_statfs, | |
+ .remount_fs = exfat_remount, | |
+ .show_options = exfat_show_options, | |
+}; | |
+ | |
+/*======================================================================*/ | |
+/* Super Block Read Operations */ | |
+/*======================================================================*/ | |
+ | |
+enum { | |
+ Opt_uid, | |
+ Opt_gid, | |
+ Opt_umask, | |
+ Opt_dmask, | |
+ Opt_fmask, | |
+ Opt_allow_utime, | |
+ Opt_codepage, | |
+ Opt_charset, | |
+ Opt_namecase, | |
+ Opt_debug, | |
+ Opt_err_cont, | |
+ Opt_err_panic, | |
+ Opt_err_ro, | |
+ Opt_err, | |
+#if EXFAT_CONFIG_DISCARD | |
+ Opt_discard, | |
+#endif /* EXFAT_CONFIG_DISCARD */ | |
+}; | |
+ | |
+static const match_table_t exfat_tokens = { | |
+ {Opt_uid, "uid=%u"}, | |
+ {Opt_gid, "gid=%u"}, | |
+ {Opt_umask, "umask=%o"}, | |
+ {Opt_dmask, "dmask=%o"}, | |
+ {Opt_fmask, "fmask=%o"}, | |
+ {Opt_allow_utime, "allow_utime=%o"}, | |
+ {Opt_codepage, "codepage=%u"}, | |
+ {Opt_charset, "iocharset=%s"}, | |
+ {Opt_namecase, "namecase=%u"}, | |
+ {Opt_debug, "debug"}, | |
+ {Opt_err_cont, "errors=continue"}, | |
+ {Opt_err_panic, "errors=panic"}, | |
+ {Opt_err_ro, "errors=remount-ro"}, | |
+#if EXFAT_CONFIG_DISCARD | |
+ {Opt_discard, "discard"}, | |
+#endif /* EXFAT_CONFIG_DISCARD */ | |
+ {Opt_err, NULL} | |
+}; | |
+ | |
+static int parse_options(char *options, int silent, int *debug, | |
+ struct exfat_mount_options *opts) | |
+{ | |
+ char *p; | |
+ substring_t args[MAX_OPT_ARGS]; | |
+ int option; | |
+ char *iocharset; | |
+ | |
+ opts->fs_uid = current_uid(); | |
+ opts->fs_gid = current_gid(); | |
+ opts->fs_fmask = opts->fs_dmask = current->fs->umask; | |
+ opts->allow_utime = (unsigned short) -1; | |
+ opts->codepage = exfat_default_codepage; | |
+ opts->iocharset = exfat_default_iocharset; | |
+ opts->casesensitive = 0; | |
+ opts->errors = EXFAT_ERRORS_RO; | |
+#if EXFAT_CONFIG_DISCARD | |
+ opts->discard = 0; | |
+#endif | |
+ *debug = 0; | |
+ | |
+ if (!options) | |
+ goto out; | |
+ | |
+ while ((p = strsep(&options, ",")) != NULL) { | |
+ int token; | |
+ if (!*p) | |
+ continue; | |
+ | |
+ token = match_token(p, exfat_tokens, args); | |
+ switch (token) { | |
+ case Opt_uid: | |
+ if (match_int(&args[0], &option)) | |
+ return 0; | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) | |
+ opts->fs_uid = KUIDT_INIT(option); | |
+#else | |
+ opts->fs_uid = option; | |
+#endif | |
+ break; | |
+ case Opt_gid: | |
+ if (match_int(&args[0], &option)) | |
+ return 0; | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) | |
+ opts->fs_gid = KGIDT_INIT(option); | |
+#else | |
+ opts->fs_gid = option; | |
+#endif | |
+ break; | |
+ case Opt_umask: | |
+ case Opt_dmask: | |
+ case Opt_fmask: | |
+ if (match_octal(&args[0], &option)) | |
+ return 0; | |
+ if (token != Opt_dmask) | |
+ opts->fs_fmask = option; | |
+ if (token != Opt_fmask) | |
+ opts->fs_dmask = option; | |
+ break; | |
+ case Opt_allow_utime: | |
+ if (match_octal(&args[0], &option)) | |
+ return 0; | |
+ opts->allow_utime = option & (S_IWGRP | S_IWOTH); | |
+ break; | |
+ case Opt_codepage: | |
+ if (match_int(&args[0], &option)) | |
+ return 0; | |
+ opts->codepage = option; | |
+ break; | |
+ case Opt_charset: | |
+ if (opts->iocharset != exfat_default_iocharset) | |
+ kfree(opts->iocharset); | |
+ iocharset = match_strdup(&args[0]); | |
+ if (!iocharset) | |
+ return -ENOMEM; | |
+ opts->iocharset = iocharset; | |
+ break; | |
+ case Opt_namecase: | |
+ if (match_int(&args[0], &option)) | |
+ return 0; | |
+ opts->casesensitive = option; | |
+ break; | |
+ case Opt_err_cont: | |
+ opts->errors = EXFAT_ERRORS_CONT; | |
+ break; | |
+ case Opt_err_panic: | |
+ opts->errors = EXFAT_ERRORS_PANIC; | |
+ break; | |
+ case Opt_err_ro: | |
+ opts->errors = EXFAT_ERRORS_RO; | |
+ break; | |
+ case Opt_debug: | |
+ *debug = 1; | |
+ break; | |
+#if EXFAT_CONFIG_DISCARD | |
+ case Opt_discard: | |
+ opts->discard = 1; | |
+ break; | |
+#endif /* EXFAT_CONFIG_DISCARD */ | |
+ default: | |
+ if (!silent) { | |
+ printk(KERN_ERR "[EXFAT] Unrecognized mount option %s or missing value\n", p); | |
+ } | |
+ return -EINVAL; | |
+ } | |
+ } | |
+ | |
+out: | |
+ if (opts->allow_utime == (unsigned short) -1) | |
+ opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void exfat_hash_init(struct super_block *sb) | |
+{ | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ int i; | |
+ | |
+ spin_lock_init(&sbi->inode_hash_lock); | |
+ for (i = 0; i < EXFAT_HASH_SIZE; i++) | |
+ INIT_HLIST_HEAD(&sbi->inode_hashtable[i]); | |
+} | |
+ | |
+static int exfat_read_root(struct inode *inode) | |
+{ | |
+ struct super_block *sb = inode->i_sb; | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ struct timespec ts; | |
+ FS_INFO_T *p_fs = &(sbi->fs_info); | |
+ DIR_ENTRY_T info; | |
+ | |
+ ts = CURRENT_TIME_SEC; | |
+ | |
+ EXFAT_I(inode)->fid.dir.dir = p_fs->root_dir; | |
+ EXFAT_I(inode)->fid.dir.flags = 0x01; | |
+ EXFAT_I(inode)->fid.entry = -1; | |
+ EXFAT_I(inode)->fid.start_clu = p_fs->root_dir; | |
+ EXFAT_I(inode)->fid.flags = 0x01; | |
+ EXFAT_I(inode)->fid.type = TYPE_DIR; | |
+ EXFAT_I(inode)->fid.rwoffset = 0; | |
+ EXFAT_I(inode)->fid.hint_last_off = -1; | |
+ | |
+ EXFAT_I(inode)->target = NULL; | |
+ | |
+ FsReadStat(inode, &info); | |
+ | |
+ inode->i_uid = sbi->options.fs_uid; | |
+ inode->i_gid = sbi->options.fs_gid; | |
+ inode->i_version++; | |
+ inode->i_generation = 0; | |
+ inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, S_IRWXUGO); | |
+ inode->i_op = &exfat_dir_inode_operations; | |
+ inode->i_fop = &exfat_dir_operations; | |
+ | |
+ i_size_write(inode, info.Size); | |
+ inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) | |
+ & ~((loff_t)p_fs->cluster_size - 1)) >> 9; | |
+ EXFAT_I(inode)->i_pos = ((loff_t) p_fs->root_dir << 32) | 0xffffffff; | |
+ EXFAT_I(inode)->mmu_private = i_size_read(inode); | |
+ | |
+ exfat_save_attr(inode, ATTR_SUBDIR); | |
+ inode->i_mtime = inode->i_atime = inode->i_ctime = ts; | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) | |
+ set_nlink(inode,info.NumSubdirs + 2); | |
+#else | |
+ inode->i_nlink = info.NumSubdirs + 2; | |
+#endif | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int exfat_fill_super(struct super_block *sb, void *data, int silent) | |
+{ | |
+ struct inode *root_inode = NULL; | |
+ struct exfat_sb_info *sbi; | |
+ int debug, ret; | |
+ long error; | |
+ char buf[50]; | |
+ | |
+ /* | |
+ * 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; | |
+ | |
+ error = parse_options(data, silent, &debug, &sbi->options); | |
+ if (error) | |
+ goto out_fail; | |
+ | |
+ 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) | |
+ */ | |
+ | |
+ 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); | |
+ if (!sbi->nls_io) { | |
+ printk(KERN_ERR "[EXFAT] IO charset %s not found\n", | |
+ sbi->options.iocharset); | |
+ goto out_fail2; | |
+ } | |
+ | |
+ error = -ENOMEM; | |
+ root_inode = new_inode(sb); | |
+ if (!root_inode) | |
+ goto out_fail2; | |
+ root_inode->i_ino = EXFAT_ROOT_INO; | |
+ root_inode->i_version = 1; | |
+ error = exfat_read_root(root_inode); | |
+ if (error < 0) | |
+ goto out_fail2; | |
+ error = -ENOMEM; | |
+ exfat_attach(root_inode, EXFAT_I(root_inode)->i_pos); | |
+ insert_inode_hash(root_inode); | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) | |
+ sb->s_root = d_make_root(root_inode); | |
+#else | |
+ sb->s_root = d_alloc_root(root_inode); | |
+#endif | |
+ if (!sb->s_root) { | |
+ printk(KERN_ERR "[EXFAT] Getting the root inode failed\n"); | |
+ goto out_fail2; | |
+ } | |
+ | |
+ return 0; | |
+ | |
+out_fail2: | |
+ FsUmountVol(sb); | |
+out_fail: | |
+ if (root_inode) | |
+ iput(root_inode); | |
+ 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) | |
+{ | |
+ kmem_cache_destroy(exfat_inode_cachep); | |
+} | |
+ | |
+#if EXFAT_CONFIG_KERNEL_DEBUG | |
+static void exfat_debug_kill_sb(struct super_block *sb) | |
+{ | |
+ struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
+ struct block_device *bdev = sb->s_bdev; | |
+ | |
+ long flags; | |
+ | |
+ if (sbi) { | |
+ flags = sbi->debug_flags; | |
+ | |
+ if (flags & EXFAT_DEBUGFLAGS_INVALID_UMOUNT) { | |
+ /* 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 /* EXFAT_CONFIG_KERNEL_DEBUG */ | |
+ | |
+static struct file_system_type exfat_fs_type = { | |
+ .owner = THIS_MODULE, | |
+ .name = "exfat", | |
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) | |
+ .get_sb = exfat_get_sb, | |
+#else | |
+ .mount = exfat_fs_mount, | |
+#endif | |
+#if EXFAT_CONFIG_KERNEL_DEBUG | |
+ .kill_sb = exfat_debug_kill_sb, | |
+#else | |
+ .kill_sb = kill_block_super, | |
+#endif /* EXFAT_CONFIG_KERNLE_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 | |
+MODULE_ALIAS_FS("exfat"); | |
+#endif | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_super.h exfat_kernel-2.6.32/fs/exfat/exfat_super.h | |
--- kernel-2.6.32/fs/exfat/exfat_super.h 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_super.h 2013-09-01 13:02:59.000000000 +1100 | |
@@ -0,0 +1,172 @@ | |
+/* Some of the source code in this file came from "linux/fs/fat/fat.h". */ | |
+ | |
+/* | |
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. | |
+ * | |
+ * This program is free software; you can redistribute it and/or | |
+ * modify it under the terms of the GNU General Public License | |
+ * as published by the Free Software Foundation; either version 2 | |
+ * of the License, or (at your option) any later version. | |
+ * | |
+ * This program is distributed in the hope that it will be useful, | |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
+ * GNU General Public License for more details. | |
+ * | |
+ * You should have received a copy of the GNU General Public License | |
+ * along with this program; if not, write to the Free Software | |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
+ */ | |
+ | |
+#ifndef _EXFAT_LINUX_H | |
+#define _EXFAT_LINUX_H | |
+ | |
+#include <linux/buffer_head.h> | |
+#include <linux/string.h> | |
+#include <linux/nls.h> | |
+#include <linux/fs.h> | |
+#include <linux/mutex.h> | |
+#include <linux/swap.h> | |
+ | |
+#include "exfat_config.h" | |
+#include "exfat_global.h" | |
+#include "exfat_data.h" | |
+#include "exfat_oal.h" | |
+ | |
+#include "exfat_blkdev.h" | |
+#include "exfat_cache.h" | |
+#include "exfat_part.h" | |
+#include "exfat_nls.h" | |
+#include "exfat_api.h" | |
+#include "exfat.h" | |
+ | |
+#define EXFAT_ERRORS_CONT 1 /* ignore error and continue */ | |
+#define EXFAT_ERRORS_PANIC 2 /* panic on error */ | |
+#define EXFAT_ERRORS_RO 3 /* remount r/o on error */ | |
+ | |
+/* ioctl command */ | |
+#define EXFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32) | |
+ | |
+struct exfat_mount_options { | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) | |
+ kuid_t fs_uid; | |
+ kgid_t fs_gid; | |
+#else | |
+ uid_t fs_uid; | |
+ gid_t fs_gid; | |
+#endif | |
+ unsigned short fs_fmask; | |
+ unsigned short fs_dmask; | |
+ unsigned short allow_utime; /* permission for setting the [am]time */ | |
+ unsigned short codepage; /* codepage for shortname conversions */ | |
+ char *iocharset; /* charset for filename input/display */ | |
+ unsigned char casesensitive; | |
+ unsigned char errors; /* on error: continue, panic, remount-ro */ | |
+#if EXFAT_CONFIG_DISCARD | |
+ unsigned char discard; /* flag on if -o dicard specified and device support discard() */ | |
+#endif /* EXFAT_CONFIG_DISCARD */ | |
+}; | |
+ | |
+#define EXFAT_HASH_BITS 8 | |
+#define EXFAT_HASH_SIZE (1UL << EXFAT_HASH_BITS) | |
+ | |
+/* | |
+ * EXFAT file system in-core superblock data | |
+ */ | |
+struct exfat_sb_info { | |
+ FS_INFO_T fs_info; | |
+ BD_INFO_T bd_info; | |
+ | |
+ struct exfat_mount_options options; | |
+ | |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) | |
+ int s_dirty; | |
+ 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]; | |
+#if EXFAT_CONFIG_KERNEL_DEBUG | |
+ long debug_flags; | |
+#endif /* EXFAT_CONFIG_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 -Naur kernel-2.6.32/fs/exfat/exfat_upcase.c exfat_kernel-2.6.32/fs/exfat/exfat_upcase.c | |
--- kernel-2.6.32/fs/exfat/exfat_upcase.c 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_upcase.c 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,408 @@ | |
+/* | |
+ * 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_global.h" | |
+ | |
+#include "exfat_nls.h" | |
+ | |
+UINT8 uni_upcase[NUM_UPCASE<<1] = { | |
+ 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, | |
+ 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0F, 0x00, | |
+ 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, | |
+ 0x18, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x1F, 0x00, | |
+ 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, | |
+ 0x28, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B, 0x00, 0x2C, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x2F, 0x00, | |
+ 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, | |
+ 0x38, 0x00, 0x39, 0x00, 0x3A, 0x00, 0x3B, 0x00, 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F, 0x00, | |
+ 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, | |
+ 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, | |
+ 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, | |
+ 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x5B, 0x00, 0x5C, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x5F, 0x00, | |
+ 0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, | |
+ 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, | |
+ 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, | |
+ 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x7B, 0x00, 0x7C, 0x00, 0x7D, 0x00, 0x7E, 0x00, 0x7F, 0x00, | |
+ 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, | |
+ 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00, 0x8C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00, | |
+ 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, | |
+ 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00, 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00, | |
+ 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, 0x00, | |
+ 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, 0x00, 0xAC, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAF, 0x00, | |
+ 0xB0, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB7, 0x00, | |
+ 0xB8, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBB, 0x00, 0xBC, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBF, 0x00, | |
+ 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, | |
+ 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, | |
+ 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD7, 0x00, | |
+ 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0xDF, 0x00, | |
+ 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, | |
+ 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, | |
+ 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xF7, 0x00, | |
+ 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0x78, 0x01, | |
+ 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01, | |
+ 0x08, 0x01, 0x08, 0x01, 0x0A, 0x01, 0x0A, 0x01, 0x0C, 0x01, 0x0C, 0x01, 0x0E, 0x01, 0x0E, 0x01, | |
+ 0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01, 0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01, | |
+ 0x18, 0x01, 0x18, 0x01, 0x1A, 0x01, 0x1A, 0x01, 0x1C, 0x01, 0x1C, 0x01, 0x1E, 0x01, 0x1E, 0x01, | |
+ 0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01, 0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01, | |
+ 0x28, 0x01, 0x28, 0x01, 0x2A, 0x01, 0x2A, 0x01, 0x2C, 0x01, 0x2C, 0x01, 0x2E, 0x01, 0x2E, 0x01, | |
+ 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01, 0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01, | |
+ 0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3B, 0x01, 0x3B, 0x01, 0x3D, 0x01, 0x3D, 0x01, 0x3F, 0x01, | |
+ 0x3F, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01, 0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01, | |
+ 0x47, 0x01, 0x49, 0x01, 0x4A, 0x01, 0x4A, 0x01, 0x4C, 0x01, 0x4C, 0x01, 0x4E, 0x01, 0x4E, 0x01, | |
+ 0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01, 0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01, | |
+ 0x58, 0x01, 0x58, 0x01, 0x5A, 0x01, 0x5A, 0x01, 0x5C, 0x01, 0x5C, 0x01, 0x5E, 0x01, 0x5E, 0x01, | |
+ 0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01, 0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01, | |
+ 0x68, 0x01, 0x68, 0x01, 0x6A, 0x01, 0x6A, 0x01, 0x6C, 0x01, 0x6C, 0x01, 0x6E, 0x01, 0x6E, 0x01, | |
+ 0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01, 0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01, | |
+ 0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7B, 0x01, 0x7B, 0x01, 0x7D, 0x01, 0x7D, 0x01, 0x7F, 0x01, | |
+ 0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01, 0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01, | |
+ 0x87, 0x01, 0x89, 0x01, 0x8A, 0x01, 0x8B, 0x01, 0x8B, 0x01, 0x8D, 0x01, 0x8E, 0x01, 0x8F, 0x01, | |
+ 0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01, 0x94, 0x01, 0xF6, 0x01, 0x96, 0x01, 0x97, 0x01, | |
+ 0x98, 0x01, 0x98, 0x01, 0x3D, 0x02, 0x9B, 0x01, 0x9C, 0x01, 0x9D, 0x01, 0x20, 0x02, 0x9F, 0x01, | |
+ 0xA0, 0x01, 0xA0, 0x01, 0xA2, 0x01, 0xA2, 0x01, 0xA4, 0x01, 0xA4, 0x01, 0xA6, 0x01, 0xA7, 0x01, | |
+ 0xA7, 0x01, 0xA9, 0x01, 0xAA, 0x01, 0xAB, 0x01, 0xAC, 0x01, 0xAC, 0x01, 0xAE, 0x01, 0xAF, 0x01, | |
+ 0xAF, 0x01, 0xB1, 0x01, 0xB2, 0x01, 0xB3, 0x01, 0xB3, 0x01, 0xB5, 0x01, 0xB5, 0x01, 0xB7, 0x01, | |
+ 0xB8, 0x01, 0xB8, 0x01, 0xBA, 0x01, 0xBB, 0x01, 0xBC, 0x01, 0xBC, 0x01, 0xBE, 0x01, 0xF7, 0x01, | |
+ 0xC0, 0x01, 0xC1, 0x01, 0xC2, 0x01, 0xC3, 0x01, 0xC4, 0x01, 0xC5, 0x01, 0xC4, 0x01, 0xC7, 0x01, | |
+ 0xC8, 0x01, 0xC7, 0x01, 0xCA, 0x01, 0xCB, 0x01, 0xCA, 0x01, 0xCD, 0x01, 0xCD, 0x01, 0xCF, 0x01, | |
+ 0xCF, 0x01, 0xD1, 0x01, 0xD1, 0x01, 0xD3, 0x01, 0xD3, 0x01, 0xD5, 0x01, 0xD5, 0x01, 0xD7, 0x01, | |
+ 0xD7, 0x01, 0xD9, 0x01, 0xD9, 0x01, 0xDB, 0x01, 0xDB, 0x01, 0x8E, 0x01, 0xDE, 0x01, 0xDE, 0x01, | |
+ 0xE0, 0x01, 0xE0, 0x01, 0xE2, 0x01, 0xE2, 0x01, 0xE4, 0x01, 0xE4, 0x01, 0xE6, 0x01, 0xE6, 0x01, | |
+ 0xE8, 0x01, 0xE8, 0x01, 0xEA, 0x01, 0xEA, 0x01, 0xEC, 0x01, 0xEC, 0x01, 0xEE, 0x01, 0xEE, 0x01, | |
+ 0xF0, 0x01, 0xF1, 0x01, 0xF2, 0x01, 0xF1, 0x01, 0xF4, 0x01, 0xF4, 0x01, 0xF6, 0x01, 0xF7, 0x01, | |
+ 0xF8, 0x01, 0xF8, 0x01, 0xFA, 0x01, 0xFA, 0x01, 0xFC, 0x01, 0xFC, 0x01, 0xFE, 0x01, 0xFE, 0x01, | |
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02, | |
+ 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x0A, 0x02, 0x0C, 0x02, 0x0C, 0x02, 0x0E, 0x02, 0x0E, 0x02, | |
+ 0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02, 0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02, | |
+ 0x18, 0x02, 0x18, 0x02, 0x1A, 0x02, 0x1A, 0x02, 0x1C, 0x02, 0x1C, 0x02, 0x1E, 0x02, 0x1E, 0x02, | |
+ 0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02, 0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02, | |
+ 0x28, 0x02, 0x28, 0x02, 0x2A, 0x02, 0x2A, 0x02, 0x2C, 0x02, 0x2C, 0x02, 0x2E, 0x02, 0x2E, 0x02, | |
+ 0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02, 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02, | |
+ 0x38, 0x02, 0x39, 0x02, 0x65, 0x2C, 0x3B, 0x02, 0x3B, 0x02, 0x3D, 0x02, 0x66, 0x2C, 0x3F, 0x02, | |
+ 0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02, 0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02, | |
+ 0x48, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x4A, 0x02, 0x4C, 0x02, 0x4C, 0x02, 0x4E, 0x02, 0x4E, 0x02, | |
+ 0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01, 0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8A, 0x01, | |
+ 0x58, 0x02, 0x8F, 0x01, 0x5A, 0x02, 0x90, 0x01, 0x5C, 0x02, 0x5D, 0x02, 0x5E, 0x02, 0x5F, 0x02, | |
+ 0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01, 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02, | |
+ 0x97, 0x01, 0x96, 0x01, 0x6A, 0x02, 0x62, 0x2C, 0x6C, 0x02, 0x6D, 0x02, 0x6E, 0x02, 0x9C, 0x01, | |
+ 0x70, 0x02, 0x71, 0x02, 0x9D, 0x01, 0x73, 0x02, 0x74, 0x02, 0x9F, 0x01, 0x76, 0x02, 0x77, 0x02, | |
+ 0x78, 0x02, 0x79, 0x02, 0x7A, 0x02, 0x7B, 0x02, 0x7C, 0x02, 0x64, 0x2C, 0x7E, 0x02, 0x7F, 0x02, | |
+ 0xA6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xA9, 0x01, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02, | |
+ 0xAE, 0x01, 0x44, 0x02, 0xB1, 0x01, 0xB2, 0x01, 0x45, 0x02, 0x8D, 0x02, 0x8E, 0x02, 0x8F, 0x02, | |
+ 0x90, 0x02, 0x91, 0x02, 0xB7, 0x01, 0x93, 0x02, 0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02, | |
+ 0x98, 0x02, 0x99, 0x02, 0x9A, 0x02, 0x9B, 0x02, 0x9C, 0x02, 0x9D, 0x02, 0x9E, 0x02, 0x9F, 0x02, | |
+ 0xA0, 0x02, 0xA1, 0x02, 0xA2, 0x02, 0xA3, 0x02, 0xA4, 0x02, 0xA5, 0x02, 0xA6, 0x02, 0xA7, 0x02, | |
+ 0xA8, 0x02, 0xA9, 0x02, 0xAA, 0x02, 0xAB, 0x02, 0xAC, 0x02, 0xAD, 0x02, 0xAE, 0x02, 0xAF, 0x02, | |
+ 0xB0, 0x02, 0xB1, 0x02, 0xB2, 0x02, 0xB3, 0x02, 0xB4, 0x02, 0xB5, 0x02, 0xB6, 0x02, 0xB7, 0x02, | |
+ 0xB8, 0x02, 0xB9, 0x02, 0xBA, 0x02, 0xBB, 0x02, 0xBC, 0x02, 0xBD, 0x02, 0xBE, 0x02, 0xBF, 0x02, | |
+ 0xC0, 0x02, 0xC1, 0x02, 0xC2, 0x02, 0xC3, 0x02, 0xC4, 0x02, 0xC5, 0x02, 0xC6, 0x02, 0xC7, 0x02, | |
+ 0xC8, 0x02, 0xC9, 0x02, 0xCA, 0x02, 0xCB, 0x02, 0xCC, 0x02, 0xCD, 0x02, 0xCE, 0x02, 0xCF, 0x02, | |
+ 0xD0, 0x02, 0xD1, 0x02, 0xD2, 0x02, 0xD3, 0x02, 0xD4, 0x02, 0xD5, 0x02, 0xD6, 0x02, 0xD7, 0x02, | |
+ 0xD8, 0x02, 0xD9, 0x02, 0xDA, 0x02, 0xDB, 0x02, 0xDC, 0x02, 0xDD, 0x02, 0xDE, 0x02, 0xDF, 0x02, | |
+ 0xE0, 0x02, 0xE1, 0x02, 0xE2, 0x02, 0xE3, 0x02, 0xE4, 0x02, 0xE5, 0x02, 0xE6, 0x02, 0xE7, 0x02, | |
+ 0xE8, 0x02, 0xE9, 0x02, 0xEA, 0x02, 0xEB, 0x02, 0xEC, 0x02, 0xED, 0x02, 0xEE, 0x02, 0xEF, 0x02, | |
+ 0xF0, 0x02, 0xF1, 0x02, 0xF2, 0x02, 0xF3, 0x02, 0xF4, 0x02, 0xF5, 0x02, 0xF6, 0x02, 0xF7, 0x02, | |
+ 0xF8, 0x02, 0xF9, 0x02, 0xFA, 0x02, 0xFB, 0x02, 0xFC, 0x02, 0xFD, 0x02, 0xFE, 0x02, 0xFF, 0x02, | |
+ 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03, | |
+ 0x08, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0B, 0x03, 0x0C, 0x03, 0x0D, 0x03, 0x0E, 0x03, 0x0F, 0x03, | |
+ 0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03, 0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03, | |
+ 0x18, 0x03, 0x19, 0x03, 0x1A, 0x03, 0x1B, 0x03, 0x1C, 0x03, 0x1D, 0x03, 0x1E, 0x03, 0x1F, 0x03, | |
+ 0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03, 0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03, | |
+ 0x28, 0x03, 0x29, 0x03, 0x2A, 0x03, 0x2B, 0x03, 0x2C, 0x03, 0x2D, 0x03, 0x2E, 0x03, 0x2F, 0x03, | |
+ 0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03, 0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03, | |
+ 0x38, 0x03, 0x39, 0x03, 0x3A, 0x03, 0x3B, 0x03, 0x3C, 0x03, 0x3D, 0x03, 0x3E, 0x03, 0x3F, 0x03, | |
+ 0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03, 0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03, | |
+ 0x48, 0x03, 0x49, 0x03, 0x4A, 0x03, 0x4B, 0x03, 0x4C, 0x03, 0x4D, 0x03, 0x4E, 0x03, 0x4F, 0x03, | |
+ 0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03, 0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03, | |
+ 0x58, 0x03, 0x59, 0x03, 0x5A, 0x03, 0x5B, 0x03, 0x5C, 0x03, 0x5D, 0x03, 0x5E, 0x03, 0x5F, 0x03, | |
+ 0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03, 0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03, | |
+ 0x68, 0x03, 0x69, 0x03, 0x6A, 0x03, 0x6B, 0x03, 0x6C, 0x03, 0x6D, 0x03, 0x6E, 0x03, 0x6F, 0x03, | |
+ 0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03, 0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03, | |
+ 0x78, 0x03, 0x79, 0x03, 0x7A, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, 0x7E, 0x03, 0x7F, 0x03, | |
+ 0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03, 0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03, | |
+ 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, 0x8B, 0x03, 0x8C, 0x03, 0x8D, 0x03, 0x8E, 0x03, 0x8F, 0x03, | |
+ 0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, | |
+ 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, | |
+ 0xA0, 0x03, 0xA1, 0x03, 0xA2, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, | |
+ 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, | |
+ 0xB0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, | |
+ 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, | |
+ 0xA0, 0x03, 0xA1, 0x03, 0xA3, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, | |
+ 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x8C, 0x03, 0x8E, 0x03, 0x8F, 0x03, 0xCF, 0x03, | |
+ 0xD0, 0x03, 0xD1, 0x03, 0xD2, 0x03, 0xD3, 0x03, 0xD4, 0x03, 0xD5, 0x03, 0xD6, 0x03, 0xD7, 0x03, | |
+ 0xD8, 0x03, 0xD8, 0x03, 0xDA, 0x03, 0xDA, 0x03, 0xDC, 0x03, 0xDC, 0x03, 0xDE, 0x03, 0xDE, 0x03, | |
+ 0xE0, 0x03, 0xE0, 0x03, 0xE2, 0x03, 0xE2, 0x03, 0xE4, 0x03, 0xE4, 0x03, 0xE6, 0x03, 0xE6, 0x03, | |
+ 0xE8, 0x03, 0xE8, 0x03, 0xEA, 0x03, 0xEA, 0x03, 0xEC, 0x03, 0xEC, 0x03, 0xEE, 0x03, 0xEE, 0x03, | |
+ 0xF0, 0x03, 0xF1, 0x03, 0xF9, 0x03, 0xF3, 0x03, 0xF4, 0x03, 0xF5, 0x03, 0xF6, 0x03, 0xF7, 0x03, | |
+ 0xF7, 0x03, 0xF9, 0x03, 0xFA, 0x03, 0xFA, 0x03, 0xFC, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, | |
+ 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, | |
+ 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, | |
+ 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, | |
+ 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, | |
+ 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, | |
+ 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, | |
+ 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, | |
+ 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, | |
+ 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, | |
+ 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, | |
+ 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, | |
+ 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, | |
+ 0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04, 0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04, | |
+ 0x68, 0x04, 0x68, 0x04, 0x6A, 0x04, 0x6A, 0x04, 0x6C, 0x04, 0x6C, 0x04, 0x6E, 0x04, 0x6E, 0x04, | |
+ 0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04, 0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04, | |
+ 0x78, 0x04, 0x78, 0x04, 0x7A, 0x04, 0x7A, 0x04, 0x7C, 0x04, 0x7C, 0x04, 0x7E, 0x04, 0x7E, 0x04, | |
+ 0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04, 0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04, | |
+ 0x88, 0x04, 0x89, 0x04, 0x8A, 0x04, 0x8A, 0x04, 0x8C, 0x04, 0x8C, 0x04, 0x8E, 0x04, 0x8E, 0x04, | |
+ 0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04, 0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04, | |
+ 0x98, 0x04, 0x98, 0x04, 0x9A, 0x04, 0x9A, 0x04, 0x9C, 0x04, 0x9C, 0x04, 0x9E, 0x04, 0x9E, 0x04, | |
+ 0xA0, 0x04, 0xA0, 0x04, 0xA2, 0x04, 0xA2, 0x04, 0xA4, 0x04, 0xA4, 0x04, 0xA6, 0x04, 0xA6, 0x04, | |
+ 0xA8, 0x04, 0xA8, 0x04, 0xAA, 0x04, 0xAA, 0x04, 0xAC, 0x04, 0xAC, 0x04, 0xAE, 0x04, 0xAE, 0x04, | |
+ 0xB0, 0x04, 0xB0, 0x04, 0xB2, 0x04, 0xB2, 0x04, 0xB4, 0x04, 0xB4, 0x04, 0xB6, 0x04, 0xB6, 0x04, | |
+ 0xB8, 0x04, 0xB8, 0x04, 0xBA, 0x04, 0xBA, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBE, 0x04, 0xBE, 0x04, | |
+ 0xC0, 0x04, 0xC1, 0x04, 0xC1, 0x04, 0xC3, 0x04, 0xC3, 0x04, 0xC5, 0x04, 0xC5, 0x04, 0xC7, 0x04, | |
+ 0xC7, 0x04, 0xC9, 0x04, 0xC9, 0x04, 0xCB, 0x04, 0xCB, 0x04, 0xCD, 0x04, 0xCD, 0x04, 0xC0, 0x04, | |
+ 0xD0, 0x04, 0xD0, 0x04, 0xD2, 0x04, 0xD2, 0x04, 0xD4, 0x04, 0xD4, 0x04, 0xD6, 0x04, 0xD6, 0x04, | |
+ 0xD8, 0x04, 0xD8, 0x04, 0xDA, 0x04, 0xDA, 0x04, 0xDC, 0x04, 0xDC, 0x04, 0xDE, 0x04, 0xDE, 0x04, | |
+ 0xE0, 0x04, 0xE0, 0x04, 0xE2, 0x04, 0xE2, 0x04, 0xE4, 0x04, 0xE4, 0x04, 0xE6, 0x04, 0xE6, 0x04, | |
+ 0xE8, 0x04, 0xE8, 0x04, 0xEA, 0x04, 0xEA, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEE, 0x04, 0xEE, 0x04, | |
+ 0xF0, 0x04, 0xF0, 0x04, 0xF2, 0x04, 0xF2, 0x04, 0xF4, 0x04, 0xF4, 0x04, 0xF6, 0x04, 0xF6, 0x04, | |
+ 0xF8, 0x04, 0xF8, 0x04, 0xFA, 0x04, 0xFA, 0x04, 0xFC, 0x04, 0xFC, 0x04, 0xFE, 0x04, 0xFE, 0x04, | |
+ 0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, | |
+ 0x08, 0x05, 0x08, 0x05, 0x0A, 0x05, 0x0A, 0x05, 0x0C, 0x05, 0x0C, 0x05, 0x0E, 0x05, 0x0E, 0x05, | |
+ 0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05, 0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05, | |
+ 0x18, 0x05, 0x19, 0x05, 0x1A, 0x05, 0x1B, 0x05, 0x1C, 0x05, 0x1D, 0x05, 0x1E, 0x05, 0x1F, 0x05, | |
+ 0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05, 0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05, | |
+ 0x28, 0x05, 0x29, 0x05, 0x2A, 0x05, 0x2B, 0x05, 0x2C, 0x05, 0x2D, 0x05, 0x2E, 0x05, 0x2F, 0x05, | |
+ 0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, | |
+ 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, | |
+ 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, | |
+ 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, | |
+ 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05, | |
+ 0x58, 0x05, 0x59, 0x05, 0x5A, 0x05, 0x5B, 0x05, 0x5C, 0x05, 0x5D, 0x05, 0x5E, 0x05, 0x5F, 0x05, | |
+ 0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, | |
+ 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, | |
+ 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, | |
+ 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, | |
+ 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xFF, 0xFF, | |
+ 0xF6, 0x17, 0x63, 0x2C, 0x7E, 0x1D, 0x7F, 0x1D, 0x80, 0x1D, 0x81, 0x1D, 0x82, 0x1D, 0x83, 0x1D, | |
+ 0x84, 0x1D, 0x85, 0x1D, 0x86, 0x1D, 0x87, 0x1D, 0x88, 0x1D, 0x89, 0x1D, 0x8A, 0x1D, 0x8B, 0x1D, | |
+ 0x8C, 0x1D, 0x8D, 0x1D, 0x8E, 0x1D, 0x8F, 0x1D, 0x90, 0x1D, 0x91, 0x1D, 0x92, 0x1D, 0x93, 0x1D, | |
+ 0x94, 0x1D, 0x95, 0x1D, 0x96, 0x1D, 0x97, 0x1D, 0x98, 0x1D, 0x99, 0x1D, 0x9A, 0x1D, 0x9B, 0x1D, | |
+ 0x9C, 0x1D, 0x9D, 0x1D, 0x9E, 0x1D, 0x9F, 0x1D, 0xA0, 0x1D, 0xA1, 0x1D, 0xA2, 0x1D, 0xA3, 0x1D, | |
+ 0xA4, 0x1D, 0xA5, 0x1D, 0xA6, 0x1D, 0xA7, 0x1D, 0xA8, 0x1D, 0xA9, 0x1D, 0xAA, 0x1D, 0xAB, 0x1D, | |
+ 0xAC, 0x1D, 0xAD, 0x1D, 0xAE, 0x1D, 0xAF, 0x1D, 0xB0, 0x1D, 0xB1, 0x1D, 0xB2, 0x1D, 0xB3, 0x1D, | |
+ 0xB4, 0x1D, 0xB5, 0x1D, 0xB6, 0x1D, 0xB7, 0x1D, 0xB8, 0x1D, 0xB9, 0x1D, 0xBA, 0x1D, 0xBB, 0x1D, | |
+ 0xBC, 0x1D, 0xBD, 0x1D, 0xBE, 0x1D, 0xBF, 0x1D, 0xC0, 0x1D, 0xC1, 0x1D, 0xC2, 0x1D, 0xC3, 0x1D, | |
+ 0xC4, 0x1D, 0xC5, 0x1D, 0xC6, 0x1D, 0xC7, 0x1D, 0xC8, 0x1D, 0xC9, 0x1D, 0xCA, 0x1D, 0xCB, 0x1D, | |
+ 0xCC, 0x1D, 0xCD, 0x1D, 0xCE, 0x1D, 0xCF, 0x1D, 0xD0, 0x1D, 0xD1, 0x1D, 0xD2, 0x1D, 0xD3, 0x1D, | |
+ 0xD4, 0x1D, 0xD5, 0x1D, 0xD6, 0x1D, 0xD7, 0x1D, 0xD8, 0x1D, 0xD9, 0x1D, 0xDA, 0x1D, 0xDB, 0x1D, | |
+ 0xDC, 0x1D, 0xDD, 0x1D, 0xDE, 0x1D, 0xDF, 0x1D, 0xE0, 0x1D, 0xE1, 0x1D, 0xE2, 0x1D, 0xE3, 0x1D, | |
+ 0xE4, 0x1D, 0xE5, 0x1D, 0xE6, 0x1D, 0xE7, 0x1D, 0xE8, 0x1D, 0xE9, 0x1D, 0xEA, 0x1D, 0xEB, 0x1D, | |
+ 0xEC, 0x1D, 0xED, 0x1D, 0xEE, 0x1D, 0xEF, 0x1D, 0xF0, 0x1D, 0xF1, 0x1D, 0xF2, 0x1D, 0xF3, 0x1D, | |
+ 0xF4, 0x1D, 0xF5, 0x1D, 0xF6, 0x1D, 0xF7, 0x1D, 0xF8, 0x1D, 0xF9, 0x1D, 0xFA, 0x1D, 0xFB, 0x1D, | |
+ 0xFC, 0x1D, 0xFD, 0x1D, 0xFE, 0x1D, 0xFF, 0x1D, 0x00, 0x1E, 0x00, 0x1E, 0x02, 0x1E, 0x02, 0x1E, | |
+ 0x04, 0x1E, 0x04, 0x1E, 0x06, 0x1E, 0x06, 0x1E, 0x08, 0x1E, 0x08, 0x1E, 0x0A, 0x1E, 0x0A, 0x1E, | |
+ 0x0C, 0x1E, 0x0C, 0x1E, 0x0E, 0x1E, 0x0E, 0x1E, 0x10, 0x1E, 0x10, 0x1E, 0x12, 0x1E, 0x12, 0x1E, | |
+ 0x14, 0x1E, 0x14, 0x1E, 0x16, 0x1E, 0x16, 0x1E, 0x18, 0x1E, 0x18, 0x1E, 0x1A, 0x1E, 0x1A, 0x1E, | |
+ 0x1C, 0x1E, 0x1C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x20, 0x1E, 0x20, 0x1E, 0x22, 0x1E, 0x22, 0x1E, | |
+ 0x24, 0x1E, 0x24, 0x1E, 0x26, 0x1E, 0x26, 0x1E, 0x28, 0x1E, 0x28, 0x1E, 0x2A, 0x1E, 0x2A, 0x1E, | |
+ 0x2C, 0x1E, 0x2C, 0x1E, 0x2E, 0x1E, 0x2E, 0x1E, 0x30, 0x1E, 0x30, 0x1E, 0x32, 0x1E, 0x32, 0x1E, | |
+ 0x34, 0x1E, 0x34, 0x1E, 0x36, 0x1E, 0x36, 0x1E, 0x38, 0x1E, 0x38, 0x1E, 0x3A, 0x1E, 0x3A, 0x1E, | |
+ 0x3C, 0x1E, 0x3C, 0x1E, 0x3E, 0x1E, 0x3E, 0x1E, 0x40, 0x1E, 0x40, 0x1E, 0x42, 0x1E, 0x42, 0x1E, | |
+ 0x44, 0x1E, 0x44, 0x1E, 0x46, 0x1E, 0x46, 0x1E, 0x48, 0x1E, 0x48, 0x1E, 0x4A, 0x1E, 0x4A, 0x1E, | |
+ 0x4C, 0x1E, 0x4C, 0x1E, 0x4E, 0x1E, 0x4E, 0x1E, 0x50, 0x1E, 0x50, 0x1E, 0x52, 0x1E, 0x52, 0x1E, | |
+ 0x54, 0x1E, 0x54, 0x1E, 0x56, 0x1E, 0x56, 0x1E, 0x58, 0x1E, 0x58, 0x1E, 0x5A, 0x1E, 0x5A, 0x1E, | |
+ 0x5C, 0x1E, 0x5C, 0x1E, 0x5E, 0x1E, 0x5E, 0x1E, 0x60, 0x1E, 0x60, 0x1E, 0x62, 0x1E, 0x62, 0x1E, | |
+ 0x64, 0x1E, 0x64, 0x1E, 0x66, 0x1E, 0x66, 0x1E, 0x68, 0x1E, 0x68, 0x1E, 0x6A, 0x1E, 0x6A, 0x1E, | |
+ 0x6C, 0x1E, 0x6C, 0x1E, 0x6E, 0x1E, 0x6E, 0x1E, 0x70, 0x1E, 0x70, 0x1E, 0x72, 0x1E, 0x72, 0x1E, | |
+ 0x74, 0x1E, 0x74, 0x1E, 0x76, 0x1E, 0x76, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x7A, 0x1E, 0x7A, 0x1E, | |
+ 0x7C, 0x1E, 0x7C, 0x1E, 0x7E, 0x1E, 0x7E, 0x1E, 0x80, 0x1E, 0x80, 0x1E, 0x82, 0x1E, 0x82, 0x1E, | |
+ 0x84, 0x1E, 0x84, 0x1E, 0x86, 0x1E, 0x86, 0x1E, 0x88, 0x1E, 0x88, 0x1E, 0x8A, 0x1E, 0x8A, 0x1E, | |
+ 0x8C, 0x1E, 0x8C, 0x1E, 0x8E, 0x1E, 0x8E, 0x1E, 0x90, 0x1E, 0x90, 0x1E, 0x92, 0x1E, 0x92, 0x1E, | |
+ 0x94, 0x1E, 0x94, 0x1E, 0x96, 0x1E, 0x97, 0x1E, 0x98, 0x1E, 0x99, 0x1E, 0x9A, 0x1E, 0x9B, 0x1E, | |
+ 0x9C, 0x1E, 0x9D, 0x1E, 0x9E, 0x1E, 0x9F, 0x1E, 0xA0, 0x1E, 0xA0, 0x1E, 0xA2, 0x1E, 0xA2, 0x1E, | |
+ 0xA4, 0x1E, 0xA4, 0x1E, 0xA6, 0x1E, 0xA6, 0x1E, 0xA8, 0x1E, 0xA8, 0x1E, 0xAA, 0x1E, 0xAA, 0x1E, | |
+ 0xAC, 0x1E, 0xAC, 0x1E, 0xAE, 0x1E, 0xAE, 0x1E, 0xB0, 0x1E, 0xB0, 0x1E, 0xB2, 0x1E, 0xB2, 0x1E, | |
+ 0xB4, 0x1E, 0xB4, 0x1E, 0xB6, 0x1E, 0xB6, 0x1E, 0xB8, 0x1E, 0xB8, 0x1E, 0xBA, 0x1E, 0xBA, 0x1E, | |
+ 0xBC, 0x1E, 0xBC, 0x1E, 0xBE, 0x1E, 0xBE, 0x1E, 0xC0, 0x1E, 0xC0, 0x1E, 0xC2, 0x1E, 0xC2, 0x1E, | |
+ 0xC4, 0x1E, 0xC4, 0x1E, 0xC6, 0x1E, 0xC6, 0x1E, 0xC8, 0x1E, 0xC8, 0x1E, 0xCA, 0x1E, 0xCA, 0x1E, | |
+ 0xCC, 0x1E, 0xCC, 0x1E, 0xCE, 0x1E, 0xCE, 0x1E, 0xD0, 0x1E, 0xD0, 0x1E, 0xD2, 0x1E, 0xD2, 0x1E, | |
+ 0xD4, 0x1E, 0xD4, 0x1E, 0xD6, 0x1E, 0xD6, 0x1E, 0xD8, 0x1E, 0xD8, 0x1E, 0xDA, 0x1E, 0xDA, 0x1E, | |
+ 0xDC, 0x1E, 0xDC, 0x1E, 0xDE, 0x1E, 0xDE, 0x1E, 0xE0, 0x1E, 0xE0, 0x1E, 0xE2, 0x1E, 0xE2, 0x1E, | |
+ 0xE4, 0x1E, 0xE4, 0x1E, 0xE6, 0x1E, 0xE6, 0x1E, 0xE8, 0x1E, 0xE8, 0x1E, 0xEA, 0x1E, 0xEA, 0x1E, | |
+ 0xEC, 0x1E, 0xEC, 0x1E, 0xEE, 0x1E, 0xEE, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF2, 0x1E, 0xF2, 0x1E, | |
+ 0xF4, 0x1E, 0xF4, 0x1E, 0xF6, 0x1E, 0xF6, 0x1E, 0xF8, 0x1E, 0xF8, 0x1E, 0xFA, 0x1E, 0xFB, 0x1E, | |
+ 0xFC, 0x1E, 0xFD, 0x1E, 0xFE, 0x1E, 0xFF, 0x1E, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, | |
+ 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, | |
+ 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, | |
+ 0x1C, 0x1F, 0x1D, 0x1F, 0x16, 0x1F, 0x17, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, | |
+ 0x1C, 0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, | |
+ 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, | |
+ 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, | |
+ 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, | |
+ 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, | |
+ 0x4C, 0x1F, 0x4D, 0x1F, 0x46, 0x1F, 0x47, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, | |
+ 0x4C, 0x1F, 0x4D, 0x1F, 0x4E, 0x1F, 0x4F, 0x1F, 0x50, 0x1F, 0x59, 0x1F, 0x52, 0x1F, 0x5B, 0x1F, | |
+ 0x54, 0x1F, 0x5D, 0x1F, 0x56, 0x1F, 0x5F, 0x1F, 0x58, 0x1F, 0x59, 0x1F, 0x5A, 0x1F, 0x5B, 0x1F, | |
+ 0x5C, 0x1F, 0x5D, 0x1F, 0x5E, 0x1F, 0x5F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, | |
+ 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, | |
+ 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, | |
+ 0xCA, 0x1F, 0xCB, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, | |
+ 0xFA, 0x1F, 0xFB, 0x1F, 0x7E, 0x1F, 0x7F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, | |
+ 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, | |
+ 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, | |
+ 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, | |
+ 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, | |
+ 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, | |
+ 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xB2, 0x1F, 0xBC, 0x1F, | |
+ 0xB4, 0x1F, 0xB5, 0x1F, 0xB6, 0x1F, 0xB7, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, | |
+ 0xBC, 0x1F, 0xBD, 0x1F, 0xBE, 0x1F, 0xBF, 0x1F, 0xC0, 0x1F, 0xC1, 0x1F, 0xC2, 0x1F, 0xC3, 0x1F, | |
+ 0xC4, 0x1F, 0xC5, 0x1F, 0xC6, 0x1F, 0xC7, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, 0xCA, 0x1F, 0xCB, 0x1F, | |
+ 0xC3, 0x1F, 0xCD, 0x1F, 0xCE, 0x1F, 0xCF, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xD2, 0x1F, 0xD3, 0x1F, | |
+ 0xD4, 0x1F, 0xD5, 0x1F, 0xD6, 0x1F, 0xD7, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, | |
+ 0xDC, 0x1F, 0xDD, 0x1F, 0xDE, 0x1F, 0xDF, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xE2, 0x1F, 0xE3, 0x1F, | |
+ 0xE4, 0x1F, 0xEC, 0x1F, 0xE6, 0x1F, 0xE7, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, | |
+ 0xEC, 0x1F, 0xED, 0x1F, 0xEE, 0x1F, 0xEF, 0x1F, 0xF0, 0x1F, 0xF1, 0x1F, 0xF2, 0x1F, 0xF3, 0x1F, | |
+ 0xF4, 0x1F, 0xF5, 0x1F, 0xF6, 0x1F, 0xF7, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xFA, 0x1F, 0xFB, 0x1F, | |
+ 0xF3, 0x1F, 0xFD, 0x1F, 0xFE, 0x1F, 0xFF, 0x1F, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20, | |
+ 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x09, 0x20, 0x0A, 0x20, 0x0B, 0x20, | |
+ 0x0C, 0x20, 0x0D, 0x20, 0x0E, 0x20, 0x0F, 0x20, 0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20, | |
+ 0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20, 0x18, 0x20, 0x19, 0x20, 0x1A, 0x20, 0x1B, 0x20, | |
+ 0x1C, 0x20, 0x1D, 0x20, 0x1E, 0x20, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20, | |
+ 0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20, 0x28, 0x20, 0x29, 0x20, 0x2A, 0x20, 0x2B, 0x20, | |
+ 0x2C, 0x20, 0x2D, 0x20, 0x2E, 0x20, 0x2F, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20, | |
+ 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20, 0x38, 0x20, 0x39, 0x20, 0x3A, 0x20, 0x3B, 0x20, | |
+ 0x3C, 0x20, 0x3D, 0x20, 0x3E, 0x20, 0x3F, 0x20, 0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20, | |
+ 0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20, 0x48, 0x20, 0x49, 0x20, 0x4A, 0x20, 0x4B, 0x20, | |
+ 0x4C, 0x20, 0x4D, 0x20, 0x4E, 0x20, 0x4F, 0x20, 0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20, | |
+ 0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20, 0x58, 0x20, 0x59, 0x20, 0x5A, 0x20, 0x5B, 0x20, | |
+ 0x5C, 0x20, 0x5D, 0x20, 0x5E, 0x20, 0x5F, 0x20, 0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20, | |
+ 0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20, 0x68, 0x20, 0x69, 0x20, 0x6A, 0x20, 0x6B, 0x20, | |
+ 0x6C, 0x20, 0x6D, 0x20, 0x6E, 0x20, 0x6F, 0x20, 0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20, | |
+ 0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20, 0x78, 0x20, 0x79, 0x20, 0x7A, 0x20, 0x7B, 0x20, | |
+ 0x7C, 0x20, 0x7D, 0x20, 0x7E, 0x20, 0x7F, 0x20, 0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20, | |
+ 0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x8A, 0x20, 0x8B, 0x20, | |
+ 0x8C, 0x20, 0x8D, 0x20, 0x8E, 0x20, 0x8F, 0x20, 0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20, | |
+ 0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20, 0x98, 0x20, 0x99, 0x20, 0x9A, 0x20, 0x9B, 0x20, | |
+ 0x9C, 0x20, 0x9D, 0x20, 0x9E, 0x20, 0x9F, 0x20, 0xA0, 0x20, 0xA1, 0x20, 0xA2, 0x20, 0xA3, 0x20, | |
+ 0xA4, 0x20, 0xA5, 0x20, 0xA6, 0x20, 0xA7, 0x20, 0xA8, 0x20, 0xA9, 0x20, 0xAA, 0x20, 0xAB, 0x20, | |
+ 0xAC, 0x20, 0xAD, 0x20, 0xAE, 0x20, 0xAF, 0x20, 0xB0, 0x20, 0xB1, 0x20, 0xB2, 0x20, 0xB3, 0x20, | |
+ 0xB4, 0x20, 0xB5, 0x20, 0xB6, 0x20, 0xB7, 0x20, 0xB8, 0x20, 0xB9, 0x20, 0xBA, 0x20, 0xBB, 0x20, | |
+ 0xBC, 0x20, 0xBD, 0x20, 0xBE, 0x20, 0xBF, 0x20, 0xC0, 0x20, 0xC1, 0x20, 0xC2, 0x20, 0xC3, 0x20, | |
+ 0xC4, 0x20, 0xC5, 0x20, 0xC6, 0x20, 0xC7, 0x20, 0xC8, 0x20, 0xC9, 0x20, 0xCA, 0x20, 0xCB, 0x20, | |
+ 0xCC, 0x20, 0xCD, 0x20, 0xCE, 0x20, 0xCF, 0x20, 0xD0, 0x20, 0xD1, 0x20, 0xD2, 0x20, 0xD3, 0x20, | |
+ 0xD4, 0x20, 0xD5, 0x20, 0xD6, 0x20, 0xD7, 0x20, 0xD8, 0x20, 0xD9, 0x20, 0xDA, 0x20, 0xDB, 0x20, | |
+ 0xDC, 0x20, 0xDD, 0x20, 0xDE, 0x20, 0xDF, 0x20, 0xE0, 0x20, 0xE1, 0x20, 0xE2, 0x20, 0xE3, 0x20, | |
+ 0xE4, 0x20, 0xE5, 0x20, 0xE6, 0x20, 0xE7, 0x20, 0xE8, 0x20, 0xE9, 0x20, 0xEA, 0x20, 0xEB, 0x20, | |
+ 0xEC, 0x20, 0xED, 0x20, 0xEE, 0x20, 0xEF, 0x20, 0xF0, 0x20, 0xF1, 0x20, 0xF2, 0x20, 0xF3, 0x20, | |
+ 0xF4, 0x20, 0xF5, 0x20, 0xF6, 0x20, 0xF7, 0x20, 0xF8, 0x20, 0xF9, 0x20, 0xFA, 0x20, 0xFB, 0x20, | |
+ 0xFC, 0x20, 0xFD, 0x20, 0xFE, 0x20, 0xFF, 0x20, 0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21, | |
+ 0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21, 0x08, 0x21, 0x09, 0x21, 0x0A, 0x21, 0x0B, 0x21, | |
+ 0x0C, 0x21, 0x0D, 0x21, 0x0E, 0x21, 0x0F, 0x21, 0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21, | |
+ 0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21, 0x18, 0x21, 0x19, 0x21, 0x1A, 0x21, 0x1B, 0x21, | |
+ 0x1C, 0x21, 0x1D, 0x21, 0x1E, 0x21, 0x1F, 0x21, 0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21, | |
+ 0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21, 0x28, 0x21, 0x29, 0x21, 0x2A, 0x21, 0x2B, 0x21, | |
+ 0x2C, 0x21, 0x2D, 0x21, 0x2E, 0x21, 0x2F, 0x21, 0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21, | |
+ 0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21, 0x38, 0x21, 0x39, 0x21, 0x3A, 0x21, 0x3B, 0x21, | |
+ 0x3C, 0x21, 0x3D, 0x21, 0x3E, 0x21, 0x3F, 0x21, 0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21, | |
+ 0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21, 0x48, 0x21, 0x49, 0x21, 0x4A, 0x21, 0x4B, 0x21, | |
+ 0x4C, 0x21, 0x4D, 0x21, 0x32, 0x21, 0x4F, 0x21, 0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21, | |
+ 0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21, 0x58, 0x21, 0x59, 0x21, 0x5A, 0x21, 0x5B, 0x21, | |
+ 0x5C, 0x21, 0x5D, 0x21, 0x5E, 0x21, 0x5F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, | |
+ 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, | |
+ 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, | |
+ 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, | |
+ 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21, | |
+ 0x83, 0x21, 0xFF, 0xFF, 0x4B, 0x03, 0xB6, 0x24, 0xB7, 0x24, 0xB8, 0x24, 0xB9, 0x24, 0xBA, 0x24, | |
+ 0xBB, 0x24, 0xBC, 0x24, 0xBD, 0x24, 0xBE, 0x24, 0xBF, 0x24, 0xC0, 0x24, 0xC1, 0x24, 0xC2, 0x24, | |
+ 0xC3, 0x24, 0xC4, 0x24, 0xC5, 0x24, 0xC6, 0x24, 0xC7, 0x24, 0xC8, 0x24, 0xC9, 0x24, 0xCA, 0x24, | |
+ 0xCB, 0x24, 0xCC, 0x24, 0xCD, 0x24, 0xCE, 0x24, 0xCF, 0x24, 0xFF, 0xFF, 0x46, 0x07, 0x00, 0x2C, | |
+ 0x01, 0x2C, 0x02, 0x2C, 0x03, 0x2C, 0x04, 0x2C, 0x05, 0x2C, 0x06, 0x2C, 0x07, 0x2C, 0x08, 0x2C, | |
+ 0x09, 0x2C, 0x0A, 0x2C, 0x0B, 0x2C, 0x0C, 0x2C, 0x0D, 0x2C, 0x0E, 0x2C, 0x0F, 0x2C, 0x10, 0x2C, | |
+ 0x11, 0x2C, 0x12, 0x2C, 0x13, 0x2C, 0x14, 0x2C, 0x15, 0x2C, 0x16, 0x2C, 0x17, 0x2C, 0x18, 0x2C, | |
+ 0x19, 0x2C, 0x1A, 0x2C, 0x1B, 0x2C, 0x1C, 0x2C, 0x1D, 0x2C, 0x1E, 0x2C, 0x1F, 0x2C, 0x20, 0x2C, | |
+ 0x21, 0x2C, 0x22, 0x2C, 0x23, 0x2C, 0x24, 0x2C, 0x25, 0x2C, 0x26, 0x2C, 0x27, 0x2C, 0x28, 0x2C, | |
+ 0x29, 0x2C, 0x2A, 0x2C, 0x2B, 0x2C, 0x2C, 0x2C, 0x2D, 0x2C, 0x2E, 0x2C, 0x5F, 0x2C, 0x60, 0x2C, | |
+ 0x60, 0x2C, 0x62, 0x2C, 0x63, 0x2C, 0x64, 0x2C, 0x65, 0x2C, 0x66, 0x2C, 0x67, 0x2C, 0x67, 0x2C, | |
+ 0x69, 0x2C, 0x69, 0x2C, 0x6B, 0x2C, 0x6B, 0x2C, 0x6D, 0x2C, 0x6E, 0x2C, 0x6F, 0x2C, 0x70, 0x2C, | |
+ 0x71, 0x2C, 0x72, 0x2C, 0x73, 0x2C, 0x74, 0x2C, 0x75, 0x2C, 0x75, 0x2C, 0x77, 0x2C, 0x78, 0x2C, | |
+ 0x79, 0x2C, 0x7A, 0x2C, 0x7B, 0x2C, 0x7C, 0x2C, 0x7D, 0x2C, 0x7E, 0x2C, 0x7F, 0x2C, 0x80, 0x2C, | |
+ 0x80, 0x2C, 0x82, 0x2C, 0x82, 0x2C, 0x84, 0x2C, 0x84, 0x2C, 0x86, 0x2C, 0x86, 0x2C, 0x88, 0x2C, | |
+ 0x88, 0x2C, 0x8A, 0x2C, 0x8A, 0x2C, 0x8C, 0x2C, 0x8C, 0x2C, 0x8E, 0x2C, 0x8E, 0x2C, 0x90, 0x2C, | |
+ 0x90, 0x2C, 0x92, 0x2C, 0x92, 0x2C, 0x94, 0x2C, 0x94, 0x2C, 0x96, 0x2C, 0x96, 0x2C, 0x98, 0x2C, | |
+ 0x98, 0x2C, 0x9A, 0x2C, 0x9A, 0x2C, 0x9C, 0x2C, 0x9C, 0x2C, 0x9E, 0x2C, 0x9E, 0x2C, 0xA0, 0x2C, | |
+ 0xA0, 0x2C, 0xA2, 0x2C, 0xA2, 0x2C, 0xA4, 0x2C, 0xA4, 0x2C, 0xA6, 0x2C, 0xA6, 0x2C, 0xA8, 0x2C, | |
+ 0xA8, 0x2C, 0xAA, 0x2C, 0xAA, 0x2C, 0xAC, 0x2C, 0xAC, 0x2C, 0xAE, 0x2C, 0xAE, 0x2C, 0xB0, 0x2C, | |
+ 0xB0, 0x2C, 0xB2, 0x2C, 0xB2, 0x2C, 0xB4, 0x2C, 0xB4, 0x2C, 0xB6, 0x2C, 0xB6, 0x2C, 0xB8, 0x2C, | |
+ 0xB8, 0x2C, 0xBA, 0x2C, 0xBA, 0x2C, 0xBC, 0x2C, 0xBC, 0x2C, 0xBE, 0x2C, 0xBE, 0x2C, 0xC0, 0x2C, | |
+ 0xC0, 0x2C, 0xC2, 0x2C, 0xC2, 0x2C, 0xC4, 0x2C, 0xC4, 0x2C, 0xC6, 0x2C, 0xC6, 0x2C, 0xC8, 0x2C, | |
+ 0xC8, 0x2C, 0xCA, 0x2C, 0xCA, 0x2C, 0xCC, 0x2C, 0xCC, 0x2C, 0xCE, 0x2C, 0xCE, 0x2C, 0xD0, 0x2C, | |
+ 0xD0, 0x2C, 0xD2, 0x2C, 0xD2, 0x2C, 0xD4, 0x2C, 0xD4, 0x2C, 0xD6, 0x2C, 0xD6, 0x2C, 0xD8, 0x2C, | |
+ 0xD8, 0x2C, 0xDA, 0x2C, 0xDA, 0x2C, 0xDC, 0x2C, 0xDC, 0x2C, 0xDE, 0x2C, 0xDE, 0x2C, 0xE0, 0x2C, | |
+ 0xE0, 0x2C, 0xE2, 0x2C, 0xE2, 0x2C, 0xE4, 0x2C, 0xE5, 0x2C, 0xE6, 0x2C, 0xE7, 0x2C, 0xE8, 0x2C, | |
+ 0xE9, 0x2C, 0xEA, 0x2C, 0xEB, 0x2C, 0xEC, 0x2C, 0xED, 0x2C, 0xEE, 0x2C, 0xEF, 0x2C, 0xF0, 0x2C, | |
+ 0xF1, 0x2C, 0xF2, 0x2C, 0xF3, 0x2C, 0xF4, 0x2C, 0xF5, 0x2C, 0xF6, 0x2C, 0xF7, 0x2C, 0xF8, 0x2C, | |
+ 0xF9, 0x2C, 0xFA, 0x2C, 0xFB, 0x2C, 0xFC, 0x2C, 0xFD, 0x2C, 0xFE, 0x2C, 0xFF, 0x2C, 0xA0, 0x10, | |
+ 0xA1, 0x10, 0xA2, 0x10, 0xA3, 0x10, 0xA4, 0x10, 0xA5, 0x10, 0xA6, 0x10, 0xA7, 0x10, 0xA8, 0x10, | |
+ 0xA9, 0x10, 0xAA, 0x10, 0xAB, 0x10, 0xAC, 0x10, 0xAD, 0x10, 0xAE, 0x10, 0xAF, 0x10, 0xB0, 0x10, | |
+ 0xB1, 0x10, 0xB2, 0x10, 0xB3, 0x10, 0xB4, 0x10, 0xB5, 0x10, 0xB6, 0x10, 0xB7, 0x10, 0xB8, 0x10, | |
+ 0xB9, 0x10, 0xBA, 0x10, 0xBB, 0x10, 0xBC, 0x10, 0xBD, 0x10, 0xBE, 0x10, 0xBF, 0x10, 0xC0, 0x10, | |
+ 0xC1, 0x10, 0xC2, 0x10, 0xC3, 0x10, 0xC4, 0x10, 0xC5, 0x10, 0xFF, 0xFF, 0x1B, 0xD2, 0x21, 0xFF, | |
+ 0x22, 0xFF, 0x23, 0xFF, 0x24, 0xFF, 0x25, 0xFF, 0x26, 0xFF, 0x27, 0xFF, 0x28, 0xFF, 0x29, 0xFF, | |
+ 0x2A, 0xFF, 0x2B, 0xFF, 0x2C, 0xFF, 0x2D, 0xFF, 0x2E, 0xFF, 0x2F, 0xFF, 0x30, 0xFF, 0x31, 0xFF, | |
+ 0x32, 0xFF, 0x33, 0xFF, 0x34, 0xFF, 0x35, 0xFF, 0x36, 0xFF, 0x37, 0xFF, 0x38, 0xFF, 0x39, 0xFF, | |
+ 0x3A, 0xFF, 0x5B, 0xFF, 0x5C, 0xFF, 0x5D, 0xFF, 0x5E, 0xFF, 0x5F, 0xFF, 0x60, 0xFF, 0x61, 0xFF, | |
+ 0x62, 0xFF, 0x63, 0xFF, 0x64, 0xFF, 0x65, 0xFF, 0x66, 0xFF, 0x67, 0xFF, 0x68, 0xFF, 0x69, 0xFF, | |
+ 0x6A, 0xFF, 0x6B, 0xFF, 0x6C, 0xFF, 0x6D, 0xFF, 0x6E, 0xFF, 0x6F, 0xFF, 0x70, 0xFF, 0x71, 0xFF, | |
+ 0x72, 0xFF, 0x73, 0xFF, 0x74, 0xFF, 0x75, 0xFF, 0x76, 0xFF, 0x77, 0xFF, 0x78, 0xFF, 0x79, 0xFF, | |
+ 0x7A, 0xFF, 0x7B, 0xFF, 0x7C, 0xFF, 0x7D, 0xFF, 0x7E, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0x81, 0xFF, | |
+ 0x82, 0xFF, 0x83, 0xFF, 0x84, 0xFF, 0x85, 0xFF, 0x86, 0xFF, 0x87, 0xFF, 0x88, 0xFF, 0x89, 0xFF, | |
+ 0x8A, 0xFF, 0x8B, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF, 0x8E, 0xFF, 0x8F, 0xFF, 0x90, 0xFF, 0x91, 0xFF, | |
+ 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, 0xFF, 0x96, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF, | |
+ 0x9A, 0xFF, 0x9B, 0xFF, 0x9C, 0xFF, 0x9D, 0xFF, 0x9E, 0xFF, 0x9F, 0xFF, 0xA0, 0xFF, 0xA1, 0xFF, | |
+ 0xA2, 0xFF, 0xA3, 0xFF, 0xA4, 0xFF, 0xA5, 0xFF, 0xA6, 0xFF, 0xA7, 0xFF, 0xA8, 0xFF, 0xA9, 0xFF, | |
+ 0xAA, 0xFF, 0xAB, 0xFF, 0xAC, 0xFF, 0xAD, 0xFF, 0xAE, 0xFF, 0xAF, 0xFF, 0xB0, 0xFF, 0xB1, 0xFF, | |
+ 0xB2, 0xFF, 0xB3, 0xFF, 0xB4, 0xFF, 0xB5, 0xFF, 0xB6, 0xFF, 0xB7, 0xFF, 0xB8, 0xFF, 0xB9, 0xFF, | |
+ 0xBA, 0xFF, 0xBB, 0xFF, 0xBC, 0xFF, 0xBD, 0xFF, 0xBE, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, | |
+ 0xC2, 0xFF, 0xC3, 0xFF, 0xC4, 0xFF, 0xC5, 0xFF, 0xC6, 0xFF, 0xC7, 0xFF, 0xC8, 0xFF, 0xC9, 0xFF, | |
+ 0xCA, 0xFF, 0xCB, 0xFF, 0xCC, 0xFF, 0xCD, 0xFF, 0xCE, 0xFF, 0xCF, 0xFF, 0xD0, 0xFF, 0xD1, 0xFF, | |
+ 0xD2, 0xFF, 0xD3, 0xFF, 0xD4, 0xFF, 0xD5, 0xFF, 0xD6, 0xFF, 0xD7, 0xFF, 0xD8, 0xFF, 0xD9, 0xFF, | |
+ 0xDA, 0xFF, 0xDB, 0xFF, 0xDC, 0xFF, 0xDD, 0xFF, 0xDE, 0xFF, 0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF, | |
+ 0xE2, 0xFF, 0xE3, 0xFF, 0xE4, 0xFF, 0xE5, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF, | |
+ 0xEA, 0xFF, 0xEB, 0xFF, 0xEC, 0xFF, 0xED, 0xFF, 0xEE, 0xFF, 0xEF, 0xFF, 0xF0, 0xFF, 0xF1, 0xFF, | |
+ 0xF2, 0xFF, 0xF3, 0xFF, 0xF4, 0xFF, 0xF5, 0xFF, 0xF6, 0xFF, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xFF, | |
+ 0xFA, 0xFF, 0xFB, 0xFF, 0xFC, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF | |
+}; | |
+ | |
+/* end of exfat_upcase.c */ | |
diff -Naur kernel-2.6.32/fs/exfat/exfat_version.h exfat_kernel-2.6.32/fs/exfat/exfat_version.h | |
--- kernel-2.6.32/fs/exfat/exfat_version.h 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/exfat_version.h 2013-08-31 23:02:03.000000000 +1100 | |
@@ -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.5" | |
diff -Naur kernel-2.6.32/fs/exfat/Kconfig exfat_kernel-2.6.32/fs/exfat/Kconfig | |
--- kernel-2.6.32/fs/exfat/Kconfig 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/Kconfig 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,19 @@ | |
+config EXFAT_FS | |
+ tristate "exFAT filesystem support" | |
+ select NLS | |
+ help | |
+ exFAT driver from Samsung | |
+ | |
+config EXFAT_DEFAULT_CODEPAGE | |
+ int "Default codepage for exFAT" | |
+ depends on EXFAT_FS | |
+ default 437 | |
+ help | |
+ This option should be set to the codepage of your exFAT filesystems. | |
+ | |
+config EXFAT_DEFAULT_IOCHARSET | |
+ string "Default iocharset for exFAT" | |
+ depends on EXFAT_FS | |
+ default "utf8" | |
+ help | |
+ Set this to the default input/output character set you'd like exFAT to use. | |
diff -Naur kernel-2.6.32/fs/exfat/Makefile exfat_kernel-2.6.32/fs/exfat/Makefile | |
--- kernel-2.6.32/fs/exfat/Makefile 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/Makefile 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,4 @@ | |
+obj-$(CONFIG_EXFAT_FS) += exfat.o | |
+ | |
+exfat-y := exfat_core.o exfat_api.o exfat_blkdev.o exfat_cache.o exfat_super.o \ | |
+ exfat_data.o exfat_global.o exfat_nls.o exfat_oal.o exfat_upcase.o | |
diff -Naur kernel-2.6.32/fs/exfat/Makefile.module exfat_kernel-2.6.32/fs/exfat/Makefile.module | |
--- kernel-2.6.32/fs/exfat/Makefile.module 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/Makefile.module 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,26 @@ | |
+# Exfat Linux kernel modules | |
+# | |
+ | |
+obj-m += exfat.o | |
+ | |
+exfat-y := exfat_core.o exfat_api.o exfat_blkdev.o exfat_cache.o exfat_super.o \ | |
+ exfat_data.o exfat_global.o exfat_nls.o exfat_oal.o exfat_upcase.o | |
+ | |
+EXTRA_FLAGS += -I$(PWD) | |
+ | |
+#KDIR := /usr/src/linux/ | |
+KDIR := /lib/modules/$(shell uname -r)/build | |
+PWD := $(shell pwd) | |
+ | |
+all: | |
+ $(MAKE) -C $(KDIR) M=$(PWD) modules | |
+ | |
+clean: | |
+ $(MAKE) -C $(KDIR) M=$(PWD) clean | |
+ | |
+help: | |
+ $(MAKE) -C $(KDIR) M=$(PWD) help | |
+ | |
+.PHONY : install | |
+install : all | |
+ sudo $(MAKE) -C $(KDIR) M=$(PWD) modules_install; sudo depmod | |
diff -Naur kernel-2.6.32/fs/exfat/README.md exfat_kernel-2.6.32/fs/exfat/README.md | |
--- kernel-2.6.32/fs/exfat/README.md 1970-01-01 10:00:00.000000000 +1000 | |
+++ exfat_kernel-2.6.32/fs/exfat/README.md 2013-08-31 23:02:03.000000000 +1100 | |
@@ -0,0 +1,73 @@ | |
+exfat-nofuse | |
+============ | |
+ | |
+Linux non-fuse read/write kernel driver for the exFAT file system.<br /> | |
+Originally ported from android kernel v3.0. | |
+ | |
+ | |
+Kudos to ksv1986 for the mutex patch!<br /> | |
+Thanks to JackNorris for being awesome and providing the clear_inode() patch.<br /> | |
+<br /> | |
+Big thanks to lqs for completing the driver! | |
+Big thanks to benpicco for fixing 3.11.y compatibility! | |
+ | |
+ | |
+Special thanks to github user AndreiLux for spreading the word about the leak!<br /> | |
+ | |
+ | |
+Installation as stand alone module: | |
+==================================== | |
+ | |
+> cp Makefile Makefile.bak | |
+> cp Makefile.module Makefile | |
+ | |
+> make KDIR="path to kernel source" CROSS_COMPILE="path to android chain tools (as linaro)/bin/SOMETHING- (see your folder for clues)" <br /> | |
+ | |
+Example how it's works for me! | |
+ | |
+> make CROSS_COMPILE=../dorimanx-SG2-I9100-Kernel/android-toolchain/bin/arm-eabi- KDIR=../dorimanx-SG2-I9100-Kernel/ <br /> | |
+ | |
+exfat.ko module file will be created in exfat source folder. and will work with kernel source you have used. | |
+ | |
+> make install | |
+ | |
+To load the driver manually, run this as root: | |
+> modprobe exfat | |
+ | |
+To add to kernel you need to do this: | |
+====================================== | |
+ | |
+cd your kernel source dir | |
+ | |
+mkdir fs/exfat | |
+ | |
+copy all files (exept .git) from exfat-nofuse to your kernel source fs/exfat/ | |
+ | |
+see | |
+https://github.com/dorimanx/Dorimanx-SG2-I9100-Kernel/commit/e8fc728a68096db9ffcebff40244ebfb60a3de18 | |
+ | |
+edit fs/Kconfig | |
+edit fs/Makefile | |
+ | |
+cd your kernel source | |
+make menuconfig | |
+ | |
+Go to: | |
+> File systems > DOS/FAT/NT > check the 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. | |
+ | |
+and you will have new module! | |
+ | |
+exfat.ko | |
+ | |
+have fun. | |
+ | |
+Free Software for the Free Minds! | |
+===================================== | |
diff -Naur kernel-2.6.32/fs/Kconfig exfat_kernel-2.6.32/fs/Kconfig | |
--- kernel-2.6.32/fs/Kconfig 2012-04-02 21:56:45.000000000 +1100 | |
+++ exfat_kernel-2.6.32/fs/Kconfig 2013-09-01 12:11:41.000000000 +1100 | |
@@ -97,6 +97,7 @@ | |
menu "DOS/FAT/NT Filesystems" | |
source "fs/fat/Kconfig" | |
+source "fs/exfat/Kconfig" | |
source "fs/ntfs/Kconfig" | |
endmenu | |
diff -Naur kernel-2.6.32/fs/libfs.c exfat_kernel-2.6.32/fs/libfs.c | |
--- kernel-2.6.32/fs/libfs.c 2012-04-02 21:56:48.000000000 +1100 | |
+++ exfat_kernel-2.6.32/fs/libfs.c 2013-09-01 02:19:43.000000000 +1100 | |
@@ -838,6 +838,47 @@ | |
} | |
EXPORT_SYMBOL(simple_fsync); | |
+/** | |
+ * generic_file_fsync - generic fsync implementation for simple filesystems | |
+ * @file: file to synchronize | |
+ * @datasync: only synchronize essential metadata if true | |
+ * | |
+ * This is a generic implementation of the fsync method for simple | |
+ * filesystems which track all non-inode metadata in the buffers list | |
+ * hanging off the address_space structure. | |
+ */ | |
+int generic_file_fsync(struct file *file, int datasync) | |
+{ | |
+ struct writeback_control wbc = { | |
+ .sync_mode = WB_SYNC_ALL, | |
+ .nr_to_write = 0, /* metadata-only; caller takes care of data */ | |
+ }; | |
+ struct inode *inode = file->f_mapping->host; | |
+ int err; | |
+ int ret; | |
+ | |
+ ret = sync_mapping_buffers(inode->i_mapping); | |
+ if (!(inode->i_state & I_DIRTY)) | |
+ return ret; | |
+ if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) | |
+ return ret; | |
+ | |
+ err = sync_inode(inode, &wbc); | |
+ if (ret == 0) | |
+ ret = err; | |
+ return ret; | |
+} | |
+EXPORT_SYMBOL(generic_file_fsync); | |
+ | |
+/* | |
+ * No-op implementation of ->fsync for in-memory filesystems. | |
+ | |
+int noop_fsync(struct file *file, int datasync) | |
+{ | |
+ return 0; | |
+}*/ | |
+ | |
+ | |
EXPORT_SYMBOL(dcache_dir_close); | |
EXPORT_SYMBOL(dcache_dir_lseek); | |
EXPORT_SYMBOL(dcache_dir_open); | |
diff -Naur kernel-2.6.32/fs/Makefile exfat_kernel-2.6.32/fs/Makefile | |
--- kernel-2.6.32/fs/Makefile 2012-04-02 21:56:45.000000000 +1100 | |
+++ exfat_kernel-2.6.32/fs/Makefile 2013-09-01 12:13:34.000000000 +1100 | |
@@ -79,6 +79,7 @@ | |
obj-$(CONFIG_CODA_FS) += coda/ | |
obj-$(CONFIG_MINIX_FS) += minix/ | |
obj-$(CONFIG_FAT_FS) += fat/ | |
+obj-$(CONFIG_EXFAT_FS) += exfat/ | |
obj-$(CONFIG_BFS_FS) += bfs/ | |
obj-$(CONFIG_ISO9660_FS) += isofs/ | |
obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ | |
diff -Naur kernel-2.6.32/include/linux/fs.h exfat_kernel-2.6.32/include/linux/fs.h | |
--- kernel-2.6.32/include/linux/fs.h 2012-04-02 21:57:17.000000000 +1100 | |
+++ exfat_kernel-2.6.32/include/linux/fs.h 2013-09-01 05:11:45.000000000 +1100 | |
@@ -2395,6 +2395,7 @@ | |
loff_t *ppos, const void *from, size_t available); | |
extern int simple_fsync(struct file *, struct dentry *, int); | |
+extern int generic_file_fsync(struct file *, int); | |
#ifdef CONFIG_MIGRATION | |
extern int buffer_migrate_page(struct address_space *, | |
diff -Naur kernel-2.6.32/scripts/kconfig/lxdialog/check-lxdialog.sh exfat_kernel-2.6.32/scripts/kconfig/lxdialog/check-lxdialog.sh | |
--- kernel-2.6.32/scripts/kconfig/lxdialog/check-lxdialog.sh 2012-04-02 21:57:02.000000000 +1100 | |
+++ exfat_kernel-2.6.32/scripts/kconfig/lxdialog/check-lxdialog.sh 2013-09-01 03:07:22.000000000 +1100 | |
@@ -4,6 +4,8 @@ | |
# What library to link | |
ldflags() | |
{ | |
+echo -lncurses | |
+exit | |
for ext in so a dylib ; do | |
for lib in ncursesw ncurses curses ; do | |
$cc -print-file-name=lib${lib}.${ext} | grep -q / |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment