From: Valdis Klētnieks <[email protected]>
The exfat code needs a lot of work to get it into "real" shape for
the fs/ part of the kernel, so put it into drivers/staging/ for now so
that it can be worked on by everyone in the community.
The full specification of the filesystem can be found at:
https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification
Signed-off-by: Valdis Kletnieks <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
MAINTAINERS | 5 +
drivers/staging/Kconfig | 2 +
drivers/staging/Makefile | 1 +
drivers/staging/exfat/Kconfig | 39 +
drivers/staging/exfat/Makefile | 10 +
drivers/staging/exfat/TODO | 12 +
drivers/staging/exfat/exfat.h | 973 ++++++
drivers/staging/exfat/exfat_blkdev.c | 136 +
drivers/staging/exfat/exfat_cache.c | 722 +++++
drivers/staging/exfat/exfat_core.c | 3704 +++++++++++++++++++++++
drivers/staging/exfat/exfat_nls.c | 404 +++
drivers/staging/exfat/exfat_super.c | 4137 ++++++++++++++++++++++++++
drivers/staging/exfat/exfat_upcase.c | 740 +++++
13 files changed, 10885 insertions(+)
create mode 100644 drivers/staging/exfat/Kconfig
create mode 100644 drivers/staging/exfat/Makefile
create mode 100644 drivers/staging/exfat/TODO
create mode 100644 drivers/staging/exfat/exfat.h
create mode 100644 drivers/staging/exfat/exfat_blkdev.c
create mode 100644 drivers/staging/exfat/exfat_cache.c
create mode 100644 drivers/staging/exfat/exfat_core.c
create mode 100644 drivers/staging/exfat/exfat_nls.c
create mode 100644 drivers/staging/exfat/exfat_super.c
create mode 100644 drivers/staging/exfat/exfat_upcase.c
diff --git a/MAINTAINERS b/MAINTAINERS
index e3242687cd19..a484b36e5117 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6097,6 +6097,11 @@ F: include/trace/events/mdio.h
F: include/uapi/linux/mdio.h
F: include/uapi/linux/mii.h
+EXFAT FILE SYSTEM
+M: Valdis Kletnieks <[email protected]>
+S: Maintained
+F: fs/exfat/
+
EXT2 FILE SYSTEM
M: Jan Kara <[email protected]>
L: [email protected]
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index d972ec8e71fb..fbdc33874780 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -118,4 +118,6 @@ source "drivers/staging/kpc2000/Kconfig"
source "drivers/staging/isdn/Kconfig"
+source "drivers/staging/exfat/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 6018b9a4a077..ca13f87b1e1b 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -49,3 +49,4 @@ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/
obj-$(CONFIG_KPC2000) += kpc2000/
obj-$(CONFIG_ISDN_CAPI) += isdn/
+obj-$(CONFIG_EXFAT_FS) += exfat/
diff --git a/drivers/staging/exfat/Kconfig b/drivers/staging/exfat/Kconfig
new file mode 100644
index 000000000000..78b32aa2ca19
--- /dev/null
+++ b/drivers/staging/exfat/Kconfig
@@ -0,0 +1,39 @@
+config EXFAT_FS
+ tristate "exFAT fs support"
+ select NLS
+ help
+ This adds support for the exFAT file system.
+
+config EXFAT_DISCARD
+ bool "enable discard support"
+ depends on EXFAT_FS
+ default y
+
+config EXFAT_DELAYED_SYNC
+ bool "enable delayed sync"
+ depends on EXFAT_FS
+ default n
+
+config EXFAT_KERNEL_DEBUG
+ bool "enable kernel debug features via ioctl"
+ depends on EXFAT_FS
+ default n
+
+config EXFAT_DEBUG_MSG
+ bool "print debug messages"
+ depends on EXFAT_FS
+ default n
+
+config EXFAT_DEFAULT_CODEPAGE
+ int "Default codepage for exFAT"
+ default 437
+ depends on EXFAT_FS
+ help
+ This option should be set to the codepage of your exFAT filesystems.
+
+config EXFAT_DEFAULT_IOCHARSET
+ string "Default iocharset for exFAT"
+ default "utf8"
+ depends on EXFAT_FS
+ help
+ Set this to the default input/output character set you'd like exFAT to use.
diff --git a/drivers/staging/exfat/Makefile b/drivers/staging/exfat/Makefile
new file mode 100644
index 000000000000..84944dfbae28
--- /dev/null
+++ b/drivers/staging/exfat/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_EXFAT_FS) += exfat.o
+
+exfat-y := exfat_core.o \
+ exfat_super.o \
+ exfat_blkdev.o \
+ exfat_cache.o \
+ exfat_nls.o \
+ exfat_upcase.o
diff --git a/drivers/staging/exfat/TODO b/drivers/staging/exfat/TODO
new file mode 100644
index 000000000000..a3eb282f9efc
--- /dev/null
+++ b/drivers/staging/exfat/TODO
@@ -0,0 +1,12 @@
+exfat_core.c - ffsReadFile - the goto err_out seem to leak a brelse().
+same for ffsWriteFile.
+
+exfat_core.c - fs_sync(sb,0) all over the place looks fishy as hell.
+There's only one place that calls it with a non-zero argument.
+
+ffsTruncateFile - if (old_size <= new_size) {
+That doesn't look right. How did it ever work? Are they relying on lazy
+block allocation when actual writes happen? If nothing else, it never
+does the 'fid->size = new_size' and do the inode update....
+
+ffsSetAttr() is just dangling in the breeze, not wired up at all...
diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h
new file mode 100644
index 000000000000..bae180e10609
--- /dev/null
+++ b/drivers/staging/exfat/exfat.h
@@ -0,0 +1,973 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ */
+
+#ifndef _EXFAT_H
+#define _EXFAT_H
+
+#include <linux/types.h>
+#include <linux/buffer_head.h>
+
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+ /* For Debugging Purpose */
+ /* IOCTL code 'f' used by
+ * - file systems typically #0~0x1F
+ * - embedded terminal devices #128~
+ * - exts for debugging purpose #99
+ * number 100 and 101 is available now but has possible conflicts
+ */
+#define EXFAT_IOC_GET_DEBUGFLAGS _IOR('f', 100, long)
+#define EXFAT_IOC_SET_DEBUGFLAGS _IOW('f', 101, long)
+
+#define EXFAT_DEBUGFLAGS_INVALID_UMOUNT 0x01
+#define EXFAT_DEBUGFLAGS_ERROR_RW 0x02
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+
+#ifdef CONFIG_EXFAT_DEBUG_MSG
+#define DEBUG 1
+#else
+#undef DEBUG
+#endif
+
+#define DENTRY_SIZE 32 /* dir entry size */
+#define DENTRY_SIZE_BITS 5
+
+/* PBR entries */
+#define PBR_SIGNATURE 0xAA55
+#define EXT_SIGNATURE 0xAA550000
+#define VOL_LABEL "NO NAME " /* size should be 11 */
+#define OEM_NAME "MSWIN4.1" /* size should be 8 */
+#define STR_FAT12 "FAT12 " /* size should be 8 */
+#define STR_FAT16 "FAT16 " /* size should be 8 */
+#define STR_FAT32 "FAT32 " /* size should be 8 */
+#define STR_EXFAT "EXFAT " /* size should be 8 */
+#define VOL_CLEAN 0x0000
+#define VOL_DIRTY 0x0002
+
+/* max number of clusters */
+#define FAT12_THRESHOLD 4087 /* 2^12 - 1 + 2 (clu 0 & 1) */
+#define FAT16_THRESHOLD 65527 /* 2^16 - 1 + 2 */
+#define FAT32_THRESHOLD 268435457 /* 2^28 - 1 + 2 */
+#define EXFAT_THRESHOLD 268435457 /* 2^28 - 1 + 2 */
+
+/* file types */
+#define TYPE_UNUSED 0x0000
+#define TYPE_DELETED 0x0001
+#define TYPE_INVALID 0x0002
+#define TYPE_CRITICAL_PRI 0x0100
+#define TYPE_BITMAP 0x0101
+#define TYPE_UPCASE 0x0102
+#define TYPE_VOLUME 0x0103
+#define TYPE_DIR 0x0104
+#define TYPE_FILE 0x011F
+#define TYPE_SYMLINK 0x015F
+#define TYPE_CRITICAL_SEC 0x0200
+#define TYPE_STREAM 0x0201
+#define TYPE_EXTEND 0x0202
+#define TYPE_ACL 0x0203
+#define TYPE_BENIGN_PRI 0x0400
+#define TYPE_GUID 0x0401
+#define TYPE_PADDING 0x0402
+#define TYPE_ACLTAB 0x0403
+#define TYPE_BENIGN_SEC 0x0800
+#define TYPE_ALL 0x0FFF
+
+/* time modes */
+#define TM_CREATE 0
+#define TM_MODIFY 1
+#define TM_ACCESS 2
+
+/* checksum types */
+#define CS_DIR_ENTRY 0
+#define CS_PBR_SECTOR 1
+#define CS_DEFAULT 2
+
+#define CLUSTER_16(x) ((u16)(x))
+#define CLUSTER_32(x) ((u32)(x))
+
+#define FALSE 0
+#define TRUE 1
+
+#define START_SECTOR(x) \
+ ((((sector_t)((x) - 2)) << p_fs->sectors_per_clu_bits) + \
+ p_fs->data_start_sector)
+
+#define IS_LAST_SECTOR_IN_CLUSTER(sec) \
+ ((((sec) - p_fs->data_start_sector + 1) & \
+ ((1 << p_fs->sectors_per_clu_bits) - 1)) == 0)
+
+#define GET_CLUSTER_FROM_SECTOR(sec) \
+ ((u32)((((sec) - p_fs->data_start_sector) >> \
+ p_fs->sectors_per_clu_bits) + 2))
+
+#define GET16(p_src) \
+ (((u16)(p_src)[0]) | (((u16)(p_src)[1]) << 8))
+#define GET32(p_src) \
+ (((u32)(p_src)[0]) | (((u32)(p_src)[1]) << 8) | \
+ (((u32)(p_src)[2]) << 16) | (((u32)(p_src)[3]) << 24))
+#define GET64(p_src) \
+ (((u64)(p_src)[0]) | (((u64)(p_src)[1]) << 8) | \
+ (((u64)(p_src)[2]) << 16) | (((u64)(p_src)[3]) << 24) | \
+ (((u64)(p_src)[4]) << 32) | (((u64)(p_src)[5]) << 40) | \
+ (((u64)(p_src)[6]) << 48) | (((u64)(p_src)[7]) << 56))
+
+#define SET16(p_dst, src) \
+ do { \
+ (p_dst)[0] = (u8)(src); \
+ (p_dst)[1] = (u8)(((u16)(src)) >> 8); \
+ } while (0)
+#define SET32(p_dst, src) \
+ do { \
+ (p_dst)[0] = (u8)(src); \
+ (p_dst)[1] = (u8)(((u32)(src)) >> 8); \
+ (p_dst)[2] = (u8)(((u32)(src)) >> 16); \
+ (p_dst)[3] = (u8)(((u32)(src)) >> 24); \
+ } while (0)
+#define SET64(p_dst, src) \
+ do { \
+ (p_dst)[0] = (u8)(src); \
+ (p_dst)[1] = (u8)(((u64)(src)) >> 8); \
+ (p_dst)[2] = (u8)(((u64)(src)) >> 16); \
+ (p_dst)[3] = (u8)(((u64)(src)) >> 24); \
+ (p_dst)[4] = (u8)(((u64)(src)) >> 32); \
+ (p_dst)[5] = (u8)(((u64)(src)) >> 40); \
+ (p_dst)[6] = (u8)(((u64)(src)) >> 48); \
+ (p_dst)[7] = (u8)(((u64)(src)) >> 56); \
+ } while (0)
+
+#ifdef __LITTLE_ENDIAN
+#define GET16_A(p_src) (*((u16 *)(p_src)))
+#define GET32_A(p_src) (*((u32 *)(p_src)))
+#define GET64_A(p_src) (*((u64 *)(p_src)))
+#define SET16_A(p_dst, src) (*((u16 *)(p_dst)) = (u16)(src))
+#define SET32_A(p_dst, src) (*((u32 *)(p_dst)) = (u32)(src))
+#define SET64_A(p_dst, src) (*((u64 *)(p_dst)) = (u64)(src))
+#else /* BIG_ENDIAN */
+#define GET16_A(p_src) GET16(p_src)
+#define GET32_A(p_src) GET32(p_src)
+#define GET64_A(p_src) GET64(p_src)
+#define SET16_A(p_dst, src) SET16(p_dst, src)
+#define SET32_A(p_dst, src) SET32(p_dst, src)
+#define SET64_A(p_dst, src) SET64(p_dst, src)
+#endif
+
+/* 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
+
+/* Upcase table macro */
+#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 u16 get_col_index(u16 i)
+{
+ return i >> LOW_INDEX_BIT;
+}
+static inline u16 get_row_index(u16 i)
+{
+ return i & ~HIGH_INDEX_MASK;
+}
+
+#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 filename including NULL */
+#define MAX_PATH_LENGTH 260 /* max len of pathname including NULL */
+#define DOS_NAME_LENGTH 11 /* DOS filename length excluding NULL */
+#define DOS_PATH_LENGTH 80 /* DOS pathname 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
+
+#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
+
+struct date_time_t {
+ u16 Year;
+ u16 Month;
+ u16 Day;
+ u16 Hour;
+ u16 Minute;
+ u16 Second;
+ u16 MilliSecond;
+};
+
+struct part_info_t {
+ u32 Offset; /* start sector number of the partition */
+ u32 Size; /* in sectors */
+};
+
+struct dev_info_t {
+ u32 SecSize; /* sector size in bytes */
+ u32 DevSize; /* block device size in sectors */
+};
+
+struct vol_info_t {
+ u32 FatType;
+ u32 ClusterSize;
+ u32 NumClusters;
+ u32 FreeClusters;
+ u32 UsedClusters;
+};
+
+/* directory structure */
+struct chain_t {
+ u32 dir;
+ s32 size;
+ u8 flags;
+};
+
+struct file_id_t {
+ struct chain_t dir;
+ s32 entry;
+ u32 type;
+ u32 attr;
+ u32 start_clu;
+ u64 size;
+ u8 flags;
+ s64 rwoffset;
+ s32 hint_last_off;
+ u32 hint_last_clu;
+};
+
+struct dir_entry_t {
+ char Name[MAX_NAME_LENGTH * MAX_CHARSET_SIZE];
+
+ /* used only for FAT12/16/32, not used for exFAT */
+ char ShortName[DOS_NAME_LENGTH + 2];
+
+ u32 Attr;
+ u64 Size;
+ u32 NumSubdirs;
+ struct date_time_t CreateTimestamp;
+ struct date_time_t ModifyTimestamp;
+ struct date_time_t AccessTimestamp;
+};
+
+struct timestamp_t {
+ u16 sec; /* 0 ~ 59 */
+ u16 min; /* 0 ~ 59 */
+ u16 hour; /* 0 ~ 23 */
+ u16 day; /* 1 ~ 31 */
+ u16 mon; /* 1 ~ 12 */
+ u16 year; /* 0 ~ 127 (since 1980) */
+};
+
+/* MS_DOS FAT partition boot record (512 bytes) */
+struct pbr_sector_t {
+ u8 jmp_boot[3];
+ u8 oem_name[8];
+ u8 bpb[109];
+ u8 boot_code[390];
+ u8 signature[2];
+};
+
+/* MS-DOS FAT12/16 BIOS parameter block (51 bytes) */
+struct bpb16_t {
+ u8 sector_size[2];
+ u8 sectors_per_clu;
+ u8 num_reserved[2];
+ u8 num_fats;
+ u8 num_root_entries[2];
+ u8 num_sectors[2];
+ u8 media_type;
+ u8 num_fat_sectors[2];
+ u8 sectors_in_track[2];
+ u8 num_heads[2];
+ u8 num_hid_sectors[4];
+ u8 num_huge_sectors[4];
+
+ u8 phy_drv_no;
+ u8 reserved;
+ u8 ext_signature;
+ u8 vol_serial[4];
+ u8 vol_label[11];
+ u8 vol_type[8];
+};
+
+/* MS-DOS FAT32 BIOS parameter block (79 bytes) */
+struct bpb32_t {
+ u8 sector_size[2];
+ u8 sectors_per_clu;
+ u8 num_reserved[2];
+ u8 num_fats;
+ u8 num_root_entries[2];
+ u8 num_sectors[2];
+ u8 media_type;
+ u8 num_fat_sectors[2];
+ u8 sectors_in_track[2];
+ u8 num_heads[2];
+ u8 num_hid_sectors[4];
+ u8 num_huge_sectors[4];
+ u8 num_fat32_sectors[4];
+ u8 ext_flags[2];
+ u8 fs_version[2];
+ u8 root_cluster[4];
+ u8 fsinfo_sector[2];
+ u8 backup_sector[2];
+ u8 reserved[12];
+
+ u8 phy_drv_no;
+ u8 ext_reserved;
+ u8 ext_signature;
+ u8 vol_serial[4];
+ u8 vol_label[11];
+ u8 vol_type[8];
+};
+
+/* MS-DOS EXFAT BIOS parameter block (109 bytes) */
+struct bpbex_t {
+ u8 reserved1[53];
+ u8 vol_offset[8];
+ u8 vol_length[8];
+ u8 fat_offset[4];
+ u8 fat_length[4];
+ u8 clu_offset[4];
+ u8 clu_count[4];
+ u8 root_cluster[4];
+ u8 vol_serial[4];
+ u8 fs_version[2];
+ u8 vol_flags[2];
+ u8 sector_size_bits;
+ u8 sectors_per_clu_bits;
+ u8 num_fats;
+ u8 phy_drv_no;
+ u8 perc_in_use;
+ u8 reserved2[7];
+};
+
+/* MS-DOS FAT file system information sector (512 bytes) */
+struct fsi_sector_t {
+ u8 signature1[4];
+ u8 reserved1[480];
+ u8 signature2[4];
+ u8 free_cluster[4];
+ u8 next_cluster[4];
+ u8 reserved2[14];
+ u8 signature3[2];
+};
+
+/* MS-DOS FAT directory entry (32 bytes) */
+struct dentry_t {
+ u8 dummy[32];
+};
+
+struct dos_dentry_t {
+ u8 name[DOS_NAME_LENGTH];
+ u8 attr;
+ u8 lcase;
+ u8 create_time_ms;
+ u8 create_time[2];
+ u8 create_date[2];
+ u8 access_date[2];
+ u8 start_clu_hi[2];
+ u8 modify_time[2];
+ u8 modify_date[2];
+ u8 start_clu_lo[2];
+ u8 size[4];
+};
+
+/* MS-DOS FAT extended directory entry (32 bytes) */
+struct ext_dentry_t {
+ u8 order;
+ u8 unicode_0_4[10];
+ u8 attr;
+ u8 sysid;
+ u8 checksum;
+ u8 unicode_5_10[12];
+ u8 start_clu[2];
+ u8 unicode_11_12[4];
+};
+
+/* MS-DOS EXFAT file directory entry (32 bytes) */
+struct file_dentry_t {
+ u8 type;
+ u8 num_ext;
+ u8 checksum[2];
+ u8 attr[2];
+ u8 reserved1[2];
+ u8 create_time[2];
+ u8 create_date[2];
+ u8 modify_time[2];
+ u8 modify_date[2];
+ u8 access_time[2];
+ u8 access_date[2];
+ u8 create_time_ms;
+ u8 modify_time_ms;
+ u8 access_time_ms;
+ u8 reserved2[9];
+};
+
+/* MS-DOS EXFAT stream extension directory entry (32 bytes) */
+struct strm_dentry_t {
+ u8 type;
+ u8 flags;
+ u8 reserved1;
+ u8 name_len;
+ u8 name_hash[2];
+ u8 reserved2[2];
+ u8 valid_size[8];
+ u8 reserved3[4];
+ u8 start_clu[4];
+ u8 size[8];
+};
+
+/* MS-DOS EXFAT file name directory entry (32 bytes) */
+struct name_dentry_t {
+ u8 type;
+ u8 flags;
+ u8 unicode_0_14[30];
+};
+
+/* MS-DOS EXFAT allocation bitmap directory entry (32 bytes) */
+struct bmap_dentry_t {
+ u8 type;
+ u8 flags;
+ u8 reserved[18];
+ u8 start_clu[4];
+ u8 size[8];
+};
+
+/* MS-DOS EXFAT up-case table directory entry (32 bytes) */
+struct case_dentry_t {
+ u8 type;
+ u8 reserved1[3];
+ u8 checksum[4];
+ u8 reserved2[12];
+ u8 start_clu[4];
+ u8 size[8];
+};
+
+/* MS-DOS EXFAT volume label directory entry (32 bytes) */
+struct volm_dentry_t {
+ u8 type;
+ u8 label_len;
+ u8 unicode_0_10[22];
+ u8 reserved[8];
+};
+
+/* unused entry hint information */
+struct uentry_t {
+ u32 dir;
+ s32 entry;
+ struct chain_t clu;
+};
+
+/* DOS name structure */
+struct dos_name_t {
+ u8 name[DOS_NAME_LENGTH];
+ u8 name_case;
+};
+
+/* unicode name structure */
+struct uni_name_t {
+ u16 name[MAX_NAME_LENGTH];
+ u16 name_hash;
+ u8 name_len;
+};
+
+struct buf_cache_t {
+ struct buf_cache_t *next;
+ struct buf_cache_t *prev;
+ struct buf_cache_t *hash_next;
+ struct buf_cache_t *hash_prev;
+ s32 drv;
+ sector_t sec;
+ u32 flag;
+ struct buffer_head *buf_bh;
+};
+
+struct fs_func {
+ s32 (*alloc_cluster)(struct super_block *sb, s32 num_alloc,
+ struct chain_t *p_chain);
+ void (*free_cluster)(struct super_block *sb, struct chain_t *p_chain,
+ s32 do_relse);
+ s32 (*count_used_clusters)(struct super_block *sb);
+
+ s32 (*init_dir_entry)(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, u32 type, u32 start_clu, u64 size);
+ s32 (*init_ext_entry)(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, s32 num_entries,
+ struct uni_name_t *p_uniname,
+ struct dos_name_t *p_dosname);
+ s32 (*find_dir_entry)(struct super_block *sb, struct chain_t *p_dir,
+ struct uni_name_t *p_uniname, s32 num_entries,
+ struct dos_name_t *p_dosname, u32 type);
+ void (*delete_dir_entry)(struct super_block *sb,
+ struct chain_t *p_dir, s32 entry,
+ s32 offset, s32 num_entries);
+ void (*get_uni_name_from_ext_entry)(struct super_block *sb,
+ struct chain_t *p_dir, s32 entry,
+ u16 *uniname);
+ s32 (*count_ext_entries)(struct super_block *sb,
+ struct chain_t *p_dir, s32 entry,
+ struct dentry_t *p_entry);
+ s32 (*calc_num_entries)(struct uni_name_t *p_uniname);
+
+ u32 (*get_entry_type)(struct dentry_t *p_entry);
+ void (*set_entry_type)(struct dentry_t *p_entry, u32 type);
+ u32 (*get_entry_attr)(struct dentry_t *p_entry);
+ void (*set_entry_attr)(struct dentry_t *p_entry, u32 attr);
+ u8 (*get_entry_flag)(struct dentry_t *p_entry);
+ void (*set_entry_flag)(struct dentry_t *p_entry, u8 flag);
+ u32 (*get_entry_clu0)(struct dentry_t *p_entry);
+ void (*set_entry_clu0)(struct dentry_t *p_entry, u32 clu0);
+ u64 (*get_entry_size)(struct dentry_t *p_entry);
+ void (*set_entry_size)(struct dentry_t *p_entry, u64 size);
+ void (*get_entry_time)(struct dentry_t *p_entry,
+ struct timestamp_t *tp, u8 mode);
+ void (*set_entry_time)(struct dentry_t *p_entry,
+ struct timestamp_t *tp, u8 mode);
+};
+
+struct fs_info_t {
+ u32 drv; /* drive ID */
+ u32 vol_type; /* volume FAT type */
+ u32 vol_id; /* volume serial number */
+
+ u64 num_sectors; /* num of sectors in volume */
+ u32 num_clusters; /* num of clusters in volume */
+ u32 cluster_size; /* cluster size in bytes */
+ u32 cluster_size_bits;
+ u32 sectors_per_clu; /* cluster size in sectors */
+ u32 sectors_per_clu_bits;
+
+ u32 PBR_sector; /* PBR sector */
+ u32 FAT1_start_sector; /* FAT1 start sector */
+ u32 FAT2_start_sector; /* FAT2 start sector */
+ u32 root_start_sector; /* root dir start sector */
+ u32 data_start_sector; /* data area start sector */
+ u32 num_FAT_sectors; /* num of FAT sectors */
+
+ u32 root_dir; /* root dir cluster */
+ u32 dentries_in_root; /* num of dentries in root dir */
+ u32 dentries_per_clu; /* num of dentries per cluster */
+
+ u32 vol_flag; /* volume dirty flag */
+ struct buffer_head *pbr_bh; /* PBR sector */
+
+ u32 map_clu; /* allocation bitmap start cluster */
+ u32 map_sectors; /* num of allocation bitmap sectors */
+ struct buffer_head **vol_amap; /* allocation bitmap */
+
+ u16 **vol_utbl; /* upcase table */
+
+ u32 clu_srch_ptr; /* cluster search pointer */
+ u32 used_clusters; /* number of used clusters */
+ struct uentry_t hint_uentry; /* unused entry hint information */
+
+ u32 dev_ejected; /* block device operation error flag */
+
+ struct fs_func *fs_func;
+ struct semaphore v_sem;
+
+ /* FAT cache */
+ struct buf_cache_t FAT_cache_array[FAT_CACHE_SIZE];
+ struct buf_cache_t FAT_cache_lru_list;
+ struct buf_cache_t FAT_cache_hash_list[FAT_CACHE_HASH_SIZE];
+
+ /* buf cache */
+ struct buf_cache_t buf_cache_array[BUF_CACHE_SIZE];
+ struct buf_cache_t buf_cache_lru_list;
+ struct buf_cache_t buf_cache_hash_list[BUF_CACHE_HASH_SIZE];
+};
+
+#define ES_2_ENTRIES 2
+#define ES_3_ENTRIES 3
+#define ES_ALL_ENTRIES 0
+
+struct entry_set_cache_t {
+ /* sector number that contains file_entry */
+ sector_t sector;
+
+ /* byte offset in the sector */
+ s32 offset;
+
+ /*
+ * flag in stream entry.
+ * 01 for cluster chain,
+ * 03 for contig. clusteres.
+ */
+ s32 alloc_flag;
+
+ u32 num_entries;
+
+ /* __buf should be the last member */
+ void *__buf;
+};
+
+#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 {
+ kuid_t fs_uid;
+ kgid_t fs_gid;
+ unsigned short fs_fmask;
+ unsigned short fs_dmask;
+
+ /* permission for setting the [am]time */
+ unsigned short allow_utime;
+
+ /* codepage for shortname conversions */
+ unsigned short codepage;
+
+ /* charset for filename input/display */
+ char *iocharset;
+
+ unsigned char casesensitive;
+
+ /* on error: continue, panic, remount-ro */
+ unsigned char errors;
+#ifdef CONFIG_EXFAT_DISCARD
+ /* flag on if -o dicard specified and device support discard() */
+ unsigned char discard;
+#endif /* CONFIG_EXFAT_DISCARD */
+};
+
+#define EXFAT_HASH_BITS 8
+#define EXFAT_HASH_SIZE (1UL << EXFAT_HASH_BITS)
+
+/*
+ * EXFAT file system in-core superblock data
+ */
+struct bd_info_t {
+ s32 sector_size; /* in bytes */
+ s32 sector_size_bits;
+ s32 sector_size_mask;
+
+ /* total number of sectors in this block device */
+ s32 num_sectors;
+
+ /* opened or not */
+ bool opened;
+};
+
+struct exfat_sb_info {
+ struct fs_info_t fs_info;
+ struct bd_info_t bd_info;
+
+ struct exfat_mount_options options;
+
+ int s_dirt;
+ struct mutex s_lock;
+ struct nls_table *nls_disk; /* Codepage used on disk */
+ struct nls_table *nls_io; /* Charset used for input and display */
+
+ struct inode *fat_inode;
+
+ spinlock_t inode_hash_lock;
+ struct hlist_head inode_hashtable[EXFAT_HASH_SIZE];
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+ long debug_flags;
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+};
+
+/*
+ * EXFAT file system inode data in memory
+ */
+struct exfat_inode_info {
+ struct 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 */
+ struct rw_semaphore truncate_lock;
+ 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);
+}
+
+/* NLS management function */
+u16 nls_upper(struct super_block *sb, u16 a);
+int nls_dosname_cmp(struct super_block *sb, u8 *a, u8 *b);
+int nls_uniname_cmp(struct super_block *sb, u16 *a, u16 *b);
+void nls_uniname_to_dosname(struct super_block *sb,
+ struct dos_name_t *p_dosname,
+ struct uni_name_t *p_uniname, bool *p_lossy);
+void nls_dosname_to_uniname(struct super_block *sb,
+ struct uni_name_t *p_uniname,
+ struct dos_name_t *p_dosname);
+void nls_uniname_to_cstring(struct super_block *sb, u8 *p_cstring,
+ struct uni_name_t *p_uniname);
+void nls_cstring_to_uniname(struct super_block *sb,
+ struct uni_name_t *p_uniname, u8 *p_cstring,
+ bool *p_lossy);
+
+/* buffer cache management */
+void buf_init(struct super_block *sb);
+void buf_shutdown(struct super_block *sb);
+int FAT_read(struct super_block *sb, u32 loc, u32 *content);
+s32 FAT_write(struct super_block *sb, u32 loc, u32 content);
+u8 *FAT_getblk(struct super_block *sb, sector_t sec);
+void FAT_modify(struct super_block *sb, sector_t sec);
+void FAT_release_all(struct super_block *sb);
+void FAT_sync(struct super_block *sb);
+u8 *buf_getblk(struct super_block *sb, sector_t sec);
+void buf_modify(struct super_block *sb, sector_t sec);
+void buf_lock(struct super_block *sb, sector_t sec);
+void buf_unlock(struct super_block *sb, sector_t sec);
+void buf_release(struct super_block *sb, sector_t sec);
+void buf_release_all(struct super_block *sb);
+void buf_sync(struct super_block *sb);
+
+/* fs management functions */
+void fs_set_vol_flags(struct super_block *sb, u32 new_flag);
+void fs_error(struct super_block *sb);
+
+/* cluster management functions */
+s32 clear_cluster(struct super_block *sb, u32 clu);
+s32 fat_alloc_cluster(struct super_block *sb, s32 num_alloc,
+ struct chain_t *p_chain);
+s32 exfat_alloc_cluster(struct super_block *sb, s32 num_alloc,
+ struct chain_t *p_chain);
+void fat_free_cluster(struct super_block *sb, struct chain_t *p_chain,
+ s32 do_relse);
+void exfat_free_cluster(struct super_block *sb, struct chain_t *p_chain,
+ s32 do_relse);
+u32 find_last_cluster(struct super_block *sb, struct chain_t *p_chain);
+s32 count_num_clusters(struct super_block *sb, struct chain_t *dir);
+s32 fat_count_used_clusters(struct super_block *sb);
+s32 exfat_count_used_clusters(struct super_block *sb);
+void exfat_chain_cont_cluster(struct super_block *sb, u32 chain, s32 len);
+
+/* allocation bitmap management functions */
+s32 load_alloc_bitmap(struct super_block *sb);
+void free_alloc_bitmap(struct super_block *sb);
+s32 set_alloc_bitmap(struct super_block *sb, u32 clu);
+s32 clr_alloc_bitmap(struct super_block *sb, u32 clu);
+u32 test_alloc_bitmap(struct super_block *sb, u32 clu);
+void sync_alloc_bitmap(struct super_block *sb);
+
+/* upcase table management functions */
+s32 load_upcase_table(struct super_block *sb);
+void free_upcase_table(struct super_block *sb);
+
+/* dir entry management functions */
+u32 fat_get_entry_type(struct dentry_t *p_entry);
+u32 exfat_get_entry_type(struct dentry_t *p_entry);
+void fat_set_entry_type(struct dentry_t *p_entry, u32 type);
+void exfat_set_entry_type(struct dentry_t *p_entry, u32 type);
+u32 fat_get_entry_attr(struct dentry_t *p_entry);
+u32 exfat_get_entry_attr(struct dentry_t *p_entry);
+void fat_set_entry_attr(struct dentry_t *p_entry, u32 attr);
+void exfat_set_entry_attr(struct dentry_t *p_entry, u32 attr);
+u8 fat_get_entry_flag(struct dentry_t *p_entry);
+u8 exfat_get_entry_flag(struct dentry_t *p_entry);
+void fat_set_entry_flag(struct dentry_t *p_entry, u8 flag);
+void exfat_set_entry_flag(struct dentry_t *p_entry, u8 flag);
+u32 fat_get_entry_clu0(struct dentry_t *p_entry);
+u32 exfat_get_entry_clu0(struct dentry_t *p_entry);
+void fat_set_entry_clu0(struct dentry_t *p_entry, u32 start_clu);
+void exfat_set_entry_clu0(struct dentry_t *p_entry, u32 start_clu);
+u64 fat_get_entry_size(struct dentry_t *p_entry);
+u64 exfat_get_entry_size(struct dentry_t *p_entry);
+void fat_set_entry_size(struct dentry_t *p_entry, u64 size);
+void exfat_set_entry_size(struct dentry_t *p_entry, u64 size);
+struct timestamp_t *tm_current(struct timestamp_t *tm);
+void fat_get_entry_time(struct dentry_t *p_entry, struct timestamp_t *tp,
+ u8 mode);
+void exfat_get_entry_time(struct dentry_t *p_entry, struct timestamp_t *tp,
+ u8 mode);
+void fat_set_entry_time(struct dentry_t *p_entry, struct timestamp_t *tp,
+ u8 mode);
+void exfat_set_entry_time(struct dentry_t *p_entry, struct timestamp_t *tp,
+ u8 mode);
+s32 fat_init_dir_entry(struct super_block *sb, struct chain_t *p_dir, s32 entry,
+ u32 type, u32 start_clu, u64 size);
+s32 exfat_init_dir_entry(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, u32 type, u32 start_clu, u64 size);
+s32 fat_init_ext_dir_entry(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, s32 num_entries,
+ struct uni_name_t *p_uniname,
+ struct dos_name_t *p_dosname);
+s32 exfat_init_ext_dir_entry(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, s32 num_entries,
+ struct uni_name_t *p_uniname,
+ struct dos_name_t *p_dosname);
+void init_dos_entry(struct dos_dentry_t *ep, u32 type, u32 start_clu);
+void init_ext_entry(struct ext_dentry_t *ep, s32 order, u8 chksum,
+ u16 *uniname);
+void init_file_entry(struct file_dentry_t *ep, u32 type);
+void init_strm_entry(struct strm_dentry_t *ep, u8 flags, u32 start_clu,
+ u64 size);
+void init_name_entry(struct name_dentry_t *ep, u16 *uniname);
+void fat_delete_dir_entry(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, s32 order, s32 num_entries);
+void exfat_delete_dir_entry(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, s32 order, s32 num_entries);
+
+s32 find_location(struct super_block *sb, struct chain_t *p_dir, s32 entry,
+ sector_t *sector, s32 *offset);
+struct dentry_t *get_entry_with_sector(struct super_block *sb, sector_t sector,
+ s32 offset);
+struct dentry_t *get_entry_in_dir(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, sector_t *sector);
+struct entry_set_cache_t *get_entry_set_in_dir(struct super_block *sb,
+ struct chain_t *p_dir, s32 entry,
+ u32 type,
+ struct dentry_t **file_ep);
+void release_entry_set(struct entry_set_cache_t *es);
+s32 write_whole_entry_set(struct super_block *sb, struct entry_set_cache_t *es);
+s32 write_partial_entries_in_entry_set(struct super_block *sb,
+ struct entry_set_cache_t *es,
+ struct dentry_t *ep, u32 count);
+s32 search_deleted_or_unused_entry(struct super_block *sb,
+ struct chain_t *p_dir, s32 num_entries);
+s32 find_empty_entry(struct inode *inode, struct chain_t *p_dir,
+ s32 num_entries);
+s32 fat_find_dir_entry(struct super_block *sb, struct chain_t *p_dir,
+ struct uni_name_t *p_uniname, s32 num_entries,
+ struct dos_name_t *p_dosname, u32 type);
+s32 exfat_find_dir_entry(struct super_block *sb, struct chain_t *p_dir,
+ struct uni_name_t *p_uniname, s32 num_entries,
+ struct dos_name_t *p_dosname, u32 type);
+s32 fat_count_ext_entries(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, struct dentry_t *p_entry);
+s32 exfat_count_ext_entries(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, struct dentry_t *p_entry);
+s32 count_dos_name_entries(struct super_block *sb, struct chain_t *p_dir,
+ u32 type);
+void update_dir_checksum(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry);
+void update_dir_checksum_with_entry_set(struct super_block *sb,
+ struct entry_set_cache_t *es);
+bool is_dir_empty(struct super_block *sb, struct chain_t *p_dir);
+
+/* name conversion functions */
+s32 get_num_entries_and_dos_name(struct super_block *sb, struct chain_t *p_dir,
+ struct uni_name_t *p_uniname, s32 *entries,
+ struct dos_name_t *p_dosname);
+void get_uni_name_from_dos_entry(struct super_block *sb,
+ struct dos_dentry_t *ep,
+ struct uni_name_t *p_uniname, u8 mode);
+void fat_get_uni_name_from_ext_entry(struct super_block *sb,
+ struct chain_t *p_dir, s32 entry,
+ u16 *uniname);
+void exfat_get_uni_name_from_ext_entry(struct super_block *sb,
+ struct chain_t *p_dir, s32 entry,
+ u16 *uniname);
+s32 extract_uni_name_from_ext_entry(struct ext_dentry_t *ep,
+ u16 *uniname, s32 order);
+s32 extract_uni_name_from_name_entry(struct name_dentry_t *ep,
+ u16 *uniname, s32 order);
+s32 fat_generate_dos_name(struct super_block *sb, struct chain_t *p_dir,
+ struct dos_name_t *p_dosname);
+void fat_attach_count_to_dos_name(u8 *dosname, s32 count);
+s32 fat_calc_num_entries(struct uni_name_t *p_uniname);
+s32 exfat_calc_num_entries(struct uni_name_t *p_uniname);
+u8 calc_checksum_1byte(void *data, s32 len, u8 chksum);
+u16 calc_checksum_2byte(void *data, s32 len, u16 chksum, s32 type);
+u32 calc_checksum_4byte(void *data, s32 len, u32 chksum, s32 type);
+
+/* name resolution functions */
+s32 resolve_path(struct inode *inode, char *path, struct chain_t *p_dir,
+ struct uni_name_t *p_uniname);
+s32 resolve_name(u8 *name, u8 **arg);
+
+/* file operation functions */
+s32 fat16_mount(struct super_block *sb, struct pbr_sector_t *p_pbr);
+s32 fat32_mount(struct super_block *sb, struct pbr_sector_t *p_pbr);
+s32 exfat_mount(struct super_block *sb, struct pbr_sector_t *p_pbr);
+s32 create_dir(struct inode *inode, struct chain_t *p_dir,
+ struct uni_name_t *p_uniname, struct file_id_t *fid);
+s32 create_file(struct inode *inode, struct chain_t *p_dir,
+ struct uni_name_t *p_uniname, u8 mode, struct file_id_t *fid);
+void remove_file(struct inode *inode, struct chain_t *p_dir, s32 entry);
+s32 rename_file(struct inode *inode, struct chain_t *p_dir, s32 old_entry,
+ struct uni_name_t *p_uniname, struct file_id_t *fid);
+s32 move_file(struct inode *inode, struct chain_t *p_olddir, s32 oldentry,
+ struct chain_t *p_newdir, struct uni_name_t *p_uniname,
+ struct file_id_t *fid);
+
+/* sector read/write functions */
+int sector_read(struct super_block *sb, sector_t sec,
+ struct buffer_head **bh, bool read);
+int sector_write(struct super_block *sb, sector_t sec,
+ struct buffer_head *bh, bool sync);
+int multi_sector_read(struct super_block *sb, sector_t sec,
+ struct buffer_head **bh, s32 num_secs, bool read);
+int multi_sector_write(struct super_block *sb, sector_t sec,
+ struct buffer_head *bh, s32 num_secs, bool sync);
+
+void bdev_open(struct super_block *sb);
+void bdev_close(struct super_block *sb);
+int bdev_read(struct super_block *sb, sector_t secno,
+ struct buffer_head **bh, u32 num_secs, bool read);
+int bdev_write(struct super_block *sb, sector_t secno,
+ struct buffer_head *bh, u32 num_secs, bool sync);
+int bdev_sync(struct super_block *sb);
+
+extern const u8 uni_upcase[];
+#endif /* _EXFAT_H */
diff --git a/drivers/staging/exfat/exfat_blkdev.c b/drivers/staging/exfat/exfat_blkdev.c
new file mode 100644
index 000000000000..f086c75e7076
--- /dev/null
+++ b/drivers/staging/exfat/exfat_blkdev.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/blkdev.h>
+#include <linux/buffer_head.h>
+#include <linux/fs.h>
+#include "exfat.h"
+
+void bdev_open(struct super_block *sb)
+{
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+
+ if (p_bd->opened)
+ return;
+
+ p_bd->sector_size = bdev_logical_block_size(sb->s_bdev);
+ p_bd->sector_size_bits = ilog2(p_bd->sector_size);
+ p_bd->sector_size_mask = p_bd->sector_size - 1;
+ p_bd->num_sectors = i_size_read(sb->s_bdev->bd_inode) >>
+ p_bd->sector_size_bits;
+ p_bd->opened = true;
+}
+
+void bdev_close(struct super_block *sb)
+{
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+
+ p_bd->opened = false;
+}
+
+int bdev_read(struct super_block *sb, sector_t secno, struct buffer_head **bh,
+ u32 num_secs, bool read)
+{
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ long flags = sbi->debug_flags;
+
+ if (flags & EXFAT_DEBUGFLAGS_ERROR_RW)
+ return FFS_MEDIAERR;
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+
+ if (!p_bd->opened)
+ return FFS_MEDIAERR;
+
+ if (*bh)
+ __brelse(*bh);
+
+ if (read)
+ *bh = __bread(sb->s_bdev, secno,
+ num_secs << p_bd->sector_size_bits);
+ else
+ *bh = __getblk(sb->s_bdev, secno,
+ num_secs << p_bd->sector_size_bits);
+
+ if (*bh)
+ return 0;
+
+ WARN(!p_fs->dev_ejected,
+ "[EXFAT] No bh, device seems wrong or to be ejected.\n");
+
+ return FFS_MEDIAERR;
+}
+
+int bdev_write(struct super_block *sb, sector_t secno, struct buffer_head *bh,
+ u32 num_secs, bool sync)
+{
+ s32 count;
+ struct buffer_head *bh2;
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ long flags = sbi->debug_flags;
+
+ if (flags & EXFAT_DEBUGFLAGS_ERROR_RW)
+ return FFS_MEDIAERR;
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+
+ if (!p_bd->opened)
+ return FFS_MEDIAERR;
+
+ if (secno == bh->b_blocknr) {
+ lock_buffer(bh);
+ set_buffer_uptodate(bh);
+ mark_buffer_dirty(bh);
+ unlock_buffer(bh);
+ if (sync && (sync_dirty_buffer(bh) != 0))
+ return FFS_MEDIAERR;
+ } else {
+ count = num_secs << p_bd->sector_size_bits;
+
+ bh2 = __getblk(sb->s_bdev, secno, count);
+ if (!bh2)
+ 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 0;
+
+no_bh:
+ WARN(!p_fs->dev_ejected,
+ "[EXFAT] No bh, device seems wrong or to be ejected.\n");
+
+ return FFS_MEDIAERR;
+}
+
+int bdev_sync(struct super_block *sb)
+{
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ long flags = sbi->debug_flags;
+
+ if (flags & EXFAT_DEBUGFLAGS_ERROR_RW)
+ return FFS_MEDIAERR;
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+
+ if (!p_bd->opened)
+ return FFS_MEDIAERR;
+
+ return sync_blockdev(sb->s_bdev);
+}
diff --git a/drivers/staging/exfat/exfat_cache.c b/drivers/staging/exfat/exfat_cache.c
new file mode 100644
index 000000000000..f05d692c2b1e
--- /dev/null
+++ b/drivers/staging/exfat/exfat_cache.c
@@ -0,0 +1,722 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/buffer_head.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include "exfat.h"
+
+#define LOCKBIT 0x01
+#define DIRTYBIT 0x02
+
+/* Local variables */
+static DEFINE_SEMAPHORE(f_sem);
+static DEFINE_SEMAPHORE(b_sem);
+
+static struct buf_cache_t *FAT_cache_find(struct super_block *sb, sector_t sec)
+{
+ s32 off;
+ struct buf_cache_t *bp, *hp;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ off = (sec +
+ (sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE - 1);
+
+ hp = &p_fs->FAT_cache_hash_list[off];
+ for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) {
+ if ((bp->drv == p_fs->drv) && (bp->sec == sec)) {
+ WARN(!bp->buf_bh,
+ "[EXFAT] FAT_cache has no bh. It will make system panic.\n");
+
+ touch_buffer(bp->buf_bh);
+ return bp;
+ }
+ }
+ return NULL;
+}
+
+static void push_to_mru(struct buf_cache_t *bp, struct buf_cache_t *list)
+{
+ bp->next = list->next;
+ bp->prev = list;
+ list->next->prev = bp;
+ list->next = bp;
+}
+
+static void push_to_lru(struct buf_cache_t *bp, struct buf_cache_t *list)
+{
+ bp->prev = list->prev;
+ bp->next = list;
+ list->prev->next = bp;
+ list->prev = bp;
+}
+
+static void move_to_mru(struct buf_cache_t *bp, struct buf_cache_t *list)
+{
+ bp->prev->next = bp->next;
+ bp->next->prev = bp->prev;
+ push_to_mru(bp, list);
+}
+
+static void move_to_lru(struct buf_cache_t *bp, struct buf_cache_t *list)
+{
+ bp->prev->next = bp->next;
+ bp->next->prev = bp->prev;
+ push_to_lru(bp, list);
+}
+
+static struct buf_cache_t *FAT_cache_get(struct super_block *sb, sector_t sec)
+{
+ struct buf_cache_t *bp;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ bp = p_fs->FAT_cache_lru_list.prev;
+
+ move_to_mru(bp, &p_fs->FAT_cache_lru_list);
+ return bp;
+}
+
+static void FAT_cache_insert_hash(struct super_block *sb,
+ struct buf_cache_t *bp)
+{
+ s32 off;
+ struct buf_cache_t *hp;
+ struct fs_info_t *p_fs;
+
+ p_fs = &(EXFAT_SB(sb)->fs_info);
+ off = (bp->sec +
+ (bp->sec >> p_fs->sectors_per_clu_bits)) &
+ (FAT_CACHE_HASH_SIZE - 1);
+
+ hp = &p_fs->FAT_cache_hash_list[off];
+ bp->hash_next = hp->hash_next;
+ bp->hash_prev = hp;
+ hp->hash_next->hash_prev = bp;
+ hp->hash_next = bp;
+}
+
+static void FAT_cache_remove_hash(struct buf_cache_t *bp)
+{
+ (bp->hash_prev)->hash_next = bp->hash_next;
+ (bp->hash_next)->hash_prev = bp->hash_prev;
+}
+
+static void buf_cache_insert_hash(struct super_block *sb,
+ struct buf_cache_t *bp)
+{
+ s32 off;
+ struct buf_cache_t *hp;
+ struct fs_info_t *p_fs;
+
+ p_fs = &(EXFAT_SB(sb)->fs_info);
+ off = (bp->sec +
+ (bp->sec >> p_fs->sectors_per_clu_bits)) &
+ (BUF_CACHE_HASH_SIZE - 1);
+
+ hp = &p_fs->buf_cache_hash_list[off];
+ bp->hash_next = hp->hash_next;
+ bp->hash_prev = hp;
+ hp->hash_next->hash_prev = bp;
+ hp->hash_next = bp;
+}
+
+static void buf_cache_remove_hash(struct buf_cache_t *bp)
+{
+ (bp->hash_prev)->hash_next = bp->hash_next;
+ (bp->hash_next)->hash_prev = bp->hash_prev;
+}
+
+void buf_init(struct super_block *sb)
+{
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ int i;
+
+ /* LRU list */
+ p_fs->FAT_cache_lru_list.next = &p_fs->FAT_cache_lru_list;
+ 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 = NULL;
+ 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;
+ 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 = NULL;
+ 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];
+ 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];
+ 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]);
+}
+
+void buf_shutdown(struct super_block *sb)
+{
+}
+
+static int __FAT_read(struct super_block *sb, u32 loc, u32 *content)
+{
+ s32 off;
+ u32 _content;
+ sector_t sec;
+ u8 *fat_sector, *fat_entry;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+
+ if (p_fs->vol_type == FAT12) {
+ sec = p_fs->FAT1_start_sector +
+ ((loc + (loc >> 1)) >> p_bd->sector_size_bits);
+ off = (loc + (loc >> 1)) & p_bd->sector_size_mask;
+
+ if (off == (p_bd->sector_size - 1)) {
+ fat_sector = FAT_getblk(sb, sec);
+ if (!fat_sector)
+ return -1;
+
+ _content = (u32)fat_sector[off];
+
+ fat_sector = FAT_getblk(sb, ++sec);
+ if (!fat_sector)
+ return -1;
+
+ _content |= (u32)fat_sector[0] << 8;
+ } else {
+ fat_sector = FAT_getblk(sb, sec);
+ if (!fat_sector)
+ return -1;
+
+ fat_entry = &fat_sector[off];
+ _content = GET16(fat_entry);
+ }
+
+ if (loc & 1)
+ _content >>= 4;
+
+ _content &= 0x00000FFF;
+
+ if (_content >= CLUSTER_16(0x0FF8)) {
+ *content = CLUSTER_32(~0);
+ return 0;
+ }
+ *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;
+ }
+ *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;
+ }
+ *content = CLUSTER_32(_content);
+ return 0;
+ } else if (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];
+ _content = GET32_A(fat_entry);
+
+ if (_content >= CLUSTER_32(0xFFFFFFF8)) {
+ *content = CLUSTER_32(~0);
+ return 0;
+ }
+ *content = CLUSTER_32(_content);
+ return 0;
+ }
+
+ /* Unknown volume type, throw in the towel and go home */
+ *content = CLUSTER_32(~0);
+ return 0;
+}
+
+/* in : sb, loc
+ * out: content
+ * returns 0 on success
+ * -1 on error
+ */
+int FAT_read(struct super_block *sb, u32 loc, u32 *content)
+{
+ s32 ret;
+
+ down(&f_sem);
+ ret = __FAT_read(sb, loc, content);
+ up(&f_sem);
+
+ return ret;
+}
+
+static s32 __FAT_write(struct super_block *sb, u32 loc, u32 content)
+{
+ s32 off;
+ sector_t sec;
+ u8 *fat_sector, *fat_entry;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+
+ if (p_fs->vol_type == FAT12) {
+ content &= 0x00000FFF;
+
+ sec = p_fs->FAT1_start_sector +
+ ((loc + (loc >> 1)) >> p_bd->sector_size_bits);
+ off = (loc + (loc >> 1)) & p_bd->sector_size_mask;
+
+ fat_sector = FAT_getblk(sb, sec);
+ if (!fat_sector)
+ return -1;
+
+ if (loc & 1) { /* odd */
+ content <<= 4;
+
+ if (off == (p_bd->sector_size - 1)) {
+ fat_sector[off] = (u8)(content |
+ (fat_sector[off] &
+ 0x0F));
+ FAT_modify(sb, sec);
+
+ fat_sector = FAT_getblk(sb, ++sec);
+ if (!fat_sector)
+ return -1;
+
+ fat_sector[0] = (u8)(content >> 8);
+ } else {
+ fat_entry = &fat_sector[off];
+ content |= GET16(fat_entry) & 0x000F;
+
+ SET16(fat_entry, content);
+ }
+ } else { /* even */
+ fat_sector[off] = (u8)(content);
+
+ if (off == (p_bd->sector_size - 1)) {
+ fat_sector[off] = (u8)(content);
+ FAT_modify(sb, sec);
+
+ fat_sector = FAT_getblk(sb, ++sec);
+ fat_sector[0] = (u8)((fat_sector[0] & 0xF0) |
+ (content >> 8));
+ } else {
+ fat_entry = &fat_sector[off];
+ content |= GET16(fat_entry) & 0xF000;
+
+ SET16(fat_entry, content);
+ }
+ }
+ }
+
+ else if (p_fs->vol_type == FAT16) {
+ content &= 0x0000FFFF;
+
+ sec = p_fs->FAT1_start_sector + (loc >>
+ (p_bd->sector_size_bits - 1));
+ off = (loc << 1) & p_bd->sector_size_mask;
+
+ fat_sector = FAT_getblk(sb, sec);
+ if (!fat_sector)
+ return -1;
+
+ fat_entry = &fat_sector[off];
+
+ SET16_A(fat_entry, content);
+ } else if (p_fs->vol_type == FAT32) {
+ content &= 0x0FFFFFFF;
+
+ sec = p_fs->FAT1_start_sector + (loc >>
+ (p_bd->sector_size_bits - 2));
+ off = (loc << 2) & p_bd->sector_size_mask;
+
+ fat_sector = FAT_getblk(sb, sec);
+ if (!fat_sector)
+ return -1;
+
+ fat_entry = &fat_sector[off];
+
+ content |= GET32_A(fat_entry) & 0xF0000000;
+
+ SET32_A(fat_entry, content);
+ } else { /* p_fs->vol_type == EXFAT */
+ sec = p_fs->FAT1_start_sector + (loc >>
+ (p_bd->sector_size_bits - 2));
+ off = (loc << 2) & p_bd->sector_size_mask;
+
+ fat_sector = FAT_getblk(sb, sec);
+ if (!fat_sector)
+ return -1;
+
+ fat_entry = &fat_sector[off];
+
+ SET32_A(fat_entry, content);
+ }
+
+ FAT_modify(sb, sec);
+ return 0;
+}
+
+int FAT_write(struct super_block *sb, u32 loc, u32 content)
+{
+ s32 ret;
+
+ down(&f_sem);
+ ret = __FAT_write(sb, loc, content);
+ up(&f_sem);
+
+ return ret;
+}
+
+u8 *FAT_getblk(struct super_block *sb, sector_t sec)
+{
+ struct buf_cache_t *bp;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ bp = FAT_cache_find(sb, sec);
+ if (bp) {
+ move_to_mru(bp, &p_fs->FAT_cache_lru_list);
+ return bp->buf_bh->b_data;
+ }
+
+ bp = FAT_cache_get(sb, sec);
+
+ FAT_cache_remove_hash(bp);
+
+ bp->drv = p_fs->drv;
+ bp->sec = sec;
+ bp->flag = 0;
+
+ FAT_cache_insert_hash(sb, bp);
+
+ if (sector_read(sb, sec, &bp->buf_bh, 1) != FFS_SUCCESS) {
+ FAT_cache_remove_hash(bp);
+ bp->drv = -1;
+ bp->sec = ~0;
+ bp->flag = 0;
+ bp->buf_bh = NULL;
+
+ move_to_lru(bp, &p_fs->FAT_cache_lru_list);
+ return NULL;
+ }
+
+ return bp->buf_bh->b_data;
+}
+
+void FAT_modify(struct super_block *sb, sector_t sec)
+{
+ struct buf_cache_t *bp;
+
+ bp = FAT_cache_find(sb, sec);
+ if (bp)
+ sector_write(sb, sec, bp->buf_bh, 0);
+}
+
+void FAT_release_all(struct super_block *sb)
+{
+ struct buf_cache_t *bp;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ down(&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;
+ }
+
+ up(&f_sem);
+}
+
+void FAT_sync(struct super_block *sb)
+{
+ struct buf_cache_t *bp;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ down(&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;
+ }
+
+ up(&f_sem);
+}
+
+static struct buf_cache_t *buf_cache_find(struct super_block *sb, sector_t sec)
+{
+ s32 off;
+ struct buf_cache_t *bp, *hp;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ off = (sec + (sec >> p_fs->sectors_per_clu_bits)) &
+ (BUF_CACHE_HASH_SIZE - 1);
+
+ hp = &p_fs->buf_cache_hash_list[off];
+ for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) {
+ if ((bp->drv == p_fs->drv) && (bp->sec == sec)) {
+ touch_buffer(bp->buf_bh);
+ return bp;
+ }
+ }
+ return NULL;
+}
+
+static struct buf_cache_t *buf_cache_get(struct super_block *sb, sector_t sec)
+{
+ struct buf_cache_t *bp;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ bp = p_fs->buf_cache_lru_list.prev;
+ while (bp->flag & LOCKBIT)
+ bp = bp->prev;
+
+ move_to_mru(bp, &p_fs->buf_cache_lru_list);
+ return bp;
+}
+
+static u8 *__buf_getblk(struct super_block *sb, sector_t sec)
+{
+ struct buf_cache_t *bp;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ bp = buf_cache_find(sb, sec);
+ if (bp) {
+ 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;
+}
+
+u8 *buf_getblk(struct super_block *sb, sector_t sec)
+{
+ u8 *buf;
+
+ down(&b_sem);
+ buf = __buf_getblk(sb, sec);
+ up(&b_sem);
+
+ return buf;
+}
+
+void buf_modify(struct super_block *sb, sector_t sec)
+{
+ struct buf_cache_t *bp;
+
+ down(&b_sem);
+
+ bp = buf_cache_find(sb, sec);
+ if (likely(bp))
+ sector_write(sb, sec, bp->buf_bh, 0);
+
+ WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%llu).\n",
+ (unsigned long long)sec);
+
+ up(&b_sem);
+}
+
+void buf_lock(struct super_block *sb, sector_t sec)
+{
+ struct buf_cache_t *bp;
+
+ down(&b_sem);
+
+ bp = buf_cache_find(sb, sec);
+ if (likely(bp))
+ bp->flag |= LOCKBIT;
+
+ WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%llu).\n",
+ (unsigned long long)sec);
+
+ up(&b_sem);
+}
+
+void buf_unlock(struct super_block *sb, sector_t sec)
+{
+ struct buf_cache_t *bp;
+
+ down(&b_sem);
+
+ bp = buf_cache_find(sb, sec);
+ if (likely(bp))
+ bp->flag &= ~(LOCKBIT);
+
+ WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%llu).\n",
+ (unsigned long long)sec);
+
+ up(&b_sem);
+}
+
+void buf_release(struct super_block *sb, sector_t sec)
+{
+ struct buf_cache_t *bp;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ down(&b_sem);
+
+ bp = buf_cache_find(sb, sec);
+ if (likely(bp)) {
+ 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);
+ }
+
+ up(&b_sem);
+}
+
+void buf_release_all(struct super_block *sb)
+{
+ struct buf_cache_t *bp;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ down(&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;
+ }
+
+ up(&b_sem);
+}
+
+void buf_sync(struct super_block *sb)
+{
+ struct buf_cache_t *bp;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ down(&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;
+ }
+
+ up(&b_sem);
+}
diff --git a/drivers/staging/exfat/exfat_core.c b/drivers/staging/exfat/exfat_core.c
new file mode 100644
index 000000000000..9f76ca175c80
--- /dev/null
+++ b/drivers/staging/exfat/exfat_core.c
@@ -0,0 +1,3704 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/types.h>
+#include <linux/buffer_head.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+#include "exfat.h"
+
+
+static void __set_sb_dirty(struct super_block *sb)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+ sbi->s_dirt = 1;
+}
+
+static u8 name_buf[MAX_PATH_LENGTH * MAX_CHARSET_SIZE];
+
+static char *reserved_names[] = {
+ "AUX ", "CON ", "NUL ", "PRN ",
+ "COM1 ", "COM2 ", "COM3 ", "COM4 ",
+ "COM5 ", "COM6 ", "COM7 ", "COM8 ", "COM9 ",
+ "LPT1 ", "LPT2 ", "LPT3 ", "LPT4 ",
+ "LPT5 ", "LPT6 ", "LPT7 ", "LPT8 ", "LPT9 ",
+ NULL
+};
+
+static u8 free_bit[] = {
+ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, /* 0 ~ 19 */
+ 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, /* 20 ~ 39 */
+ 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 40 ~ 59 */
+ 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, /* 60 ~ 79 */
+ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, /* 80 ~ 99 */
+ 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, /* 100 ~ 119 */
+ 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 120 ~ 139 */
+ 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, /* 140 ~ 159 */
+ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, /* 160 ~ 179 */
+ 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, /* 180 ~ 199 */
+ 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 200 ~ 219 */
+ 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, /* 220 ~ 239 */
+ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 240 ~ 254 */
+};
+
+static u8 used_bit[] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, /* 0 ~ 19 */
+ 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, /* 20 ~ 39 */
+ 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, /* 40 ~ 59 */
+ 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 60 ~ 79 */
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, /* 80 ~ 99 */
+ 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, /* 100 ~ 119 */
+ 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, /* 120 ~ 139 */
+ 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 140 ~ 159 */
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, /* 160 ~ 179 */
+ 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, /* 180 ~ 199 */
+ 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, /* 200 ~ 219 */
+ 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 220 ~ 239 */
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /* 240 ~ 255 */
+};
+
+#define BITMAP_LOC(v) ((v) >> 3)
+#define BITMAP_SHIFT(v) ((v) & 0x07)
+
+static inline s32 exfat_bitmap_test(u8 *bitmap, int i)
+{
+ u8 data;
+
+ data = bitmap[BITMAP_LOC(i)];
+ if ((data >> BITMAP_SHIFT(i)) & 0x01)
+ return 1;
+ return 0;
+}
+
+static inline void exfat_bitmap_set(u8 *bitmap, int i)
+{
+ bitmap[BITMAP_LOC(i)] |= (0x01 << BITMAP_SHIFT(i));
+}
+
+static inline void exfat_bitmap_clear(u8 *bitmap, int i)
+{
+ bitmap[BITMAP_LOC(i)] &= ~(0x01 << BITMAP_SHIFT(i));
+}
+
+/*
+ * File System Management Functions
+ */
+
+void fs_set_vol_flags(struct super_block *sb, u32 new_flag)
+{
+ struct pbr_sector_t *p_pbr;
+ struct bpbex_t *p_bpb;
+ struct 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 = (struct pbr_sector_t *)p_fs->pbr_bh->b_data;
+ p_bpb = (struct bpbex_t *)p_pbr->bpb;
+ SET16(p_bpb->vol_flags, (u16)new_flag);
+
+ /* XXX duyoung
+ * what can we do here? (cuz fs_set_vol_flags() is void)
+ */
+ if ((new_flag == VOL_DIRTY) && (!buffer_dirty(p_fs->pbr_bh)))
+ sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 1);
+ else
+ sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 0);
+ }
+}
+
+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_rdonly(sb)) {
+ sb->s_flags |= SB_RDONLY;
+ pr_err("[EXFAT] Filesystem has been set read-only\n");
+ }
+}
+
+/*
+ * Cluster Management Functions
+ */
+
+s32 clear_cluster(struct super_block *sb, u32 clu)
+{
+ sector_t s, n;
+ s32 ret = FFS_SUCCESS;
+ struct buffer_head *tmp_bh = NULL;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+
+ if (clu == CLUSTER_32(0)) { /* FAT16 root_dir */
+ s = p_fs->root_start_sector;
+ n = p_fs->data_start_sector;
+ } else {
+ s = START_SECTOR(clu);
+ n = s + p_fs->sectors_per_clu;
+ }
+
+ for (; s < n; s++) {
+ ret = sector_read(sb, s, &tmp_bh, 0);
+ if (ret != FFS_SUCCESS)
+ return ret;
+
+ memset((char *)tmp_bh->b_data, 0x0, p_bd->sector_size);
+ ret = sector_write(sb, s, tmp_bh, 0);
+ if (ret != FFS_SUCCESS)
+ break;
+ }
+
+ brelse(tmp_bh);
+ return ret;
+}
+
+s32 fat_alloc_cluster(struct super_block *sb, s32 num_alloc,
+ struct chain_t *p_chain)
+{
+ int i, num_clusters = 0;
+ u32 new_clu, last_clu = CLUSTER_32(~0), read_clu;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ new_clu = p_chain->dir;
+ if (new_clu == CLUSTER_32(~0))
+ new_clu = p_fs->clu_srch_ptr;
+ else if (new_clu >= p_fs->num_clusters)
+ new_clu = 2;
+
+ __set_sb_dirty(sb);
+
+ p_chain->dir = CLUSTER_32(~0);
+
+ for (i = 2; i < p_fs->num_clusters; i++) {
+ if (FAT_read(sb, new_clu, &read_clu) != 0)
+ return -1;
+
+ if (read_clu == CLUSTER_32(0)) {
+ if (FAT_write(sb, new_clu, CLUSTER_32(~0)) < 0)
+ return -1;
+ num_clusters++;
+
+ if (p_chain->dir == CLUSTER_32(~0)) {
+ p_chain->dir = new_clu;
+ } else {
+ if (FAT_write(sb, last_clu, new_clu) < 0)
+ return -1;
+ }
+
+ last_clu = new_clu;
+
+ if ((--num_alloc) == 0) {
+ p_fs->clu_srch_ptr = new_clu;
+ if (p_fs->used_clusters != (u32) ~0)
+ p_fs->used_clusters += num_clusters;
+
+ return num_clusters;
+ }
+ }
+ if ((++new_clu) >= p_fs->num_clusters)
+ new_clu = 2;
+ }
+
+ p_fs->clu_srch_ptr = new_clu;
+ if (p_fs->used_clusters != (u32) ~0)
+ p_fs->used_clusters += num_clusters;
+
+ return num_clusters;
+}
+
+s32 exfat_alloc_cluster(struct super_block *sb, s32 num_alloc,
+ struct chain_t *p_chain)
+{
+ s32 num_clusters = 0;
+ u32 hint_clu, new_clu, last_clu = CLUSTER_32(~0);
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ hint_clu = p_chain->dir;
+ if (hint_clu == CLUSTER_32(~0)) {
+ hint_clu = test_alloc_bitmap(sb, p_fs->clu_srch_ptr-2);
+ if (hint_clu == CLUSTER_32(~0))
+ return 0;
+ } else if (hint_clu >= p_fs->num_clusters) {
+ hint_clu = 2;
+ p_chain->flags = 0x01;
+ }
+
+ __set_sb_dirty(sb);
+
+ p_chain->dir = CLUSTER_32(~0);
+
+ while ((new_clu = test_alloc_bitmap(sb, hint_clu-2)) != CLUSTER_32(~0)) {
+ if (new_clu != hint_clu) {
+ if (p_chain->flags == 0x03) {
+ exfat_chain_cont_cluster(sb, p_chain->dir,
+ num_clusters);
+ p_chain->flags = 0x01;
+ }
+ }
+
+ if (set_alloc_bitmap(sb, new_clu-2) != FFS_SUCCESS)
+ return -1;
+
+ num_clusters++;
+
+ if (p_chain->flags == 0x01) {
+ if (FAT_write(sb, new_clu, CLUSTER_32(~0)) < 0)
+ return -1;
+ }
+
+ if (p_chain->dir == CLUSTER_32(~0)) {
+ p_chain->dir = new_clu;
+ } else {
+ if (p_chain->flags == 0x01) {
+ if (FAT_write(sb, last_clu, new_clu) < 0)
+ return -1;
+ }
+ }
+ last_clu = new_clu;
+
+ if ((--num_alloc) == 0) {
+ p_fs->clu_srch_ptr = hint_clu;
+ if (p_fs->used_clusters != (u32) ~0)
+ p_fs->used_clusters += num_clusters;
+
+ p_chain->size += num_clusters;
+ return num_clusters;
+ }
+
+ hint_clu = new_clu + 1;
+ if (hint_clu >= p_fs->num_clusters) {
+ hint_clu = 2;
+
+ if (p_chain->flags == 0x03) {
+ exfat_chain_cont_cluster(sb, p_chain->dir,
+ num_clusters);
+ p_chain->flags = 0x01;
+ }
+ }
+ }
+
+ p_fs->clu_srch_ptr = hint_clu;
+ if (p_fs->used_clusters != (u32) ~0)
+ p_fs->used_clusters += num_clusters;
+
+ p_chain->size += num_clusters;
+ return num_clusters;
+}
+
+void fat_free_cluster(struct super_block *sb, struct chain_t *p_chain,
+ s32 do_relse)
+{
+ s32 num_clusters = 0;
+ u32 clu, prev;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ int i;
+ sector_t sector;
+
+ if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0)))
+ return;
+ __set_sb_dirty(sb);
+ clu = p_chain->dir;
+
+ if (p_chain->size <= 0)
+ return;
+
+ do {
+ if (p_fs->dev_ejected)
+ break;
+
+ if (do_relse) {
+ sector = START_SECTOR(clu);
+ for (i = 0; i < p_fs->sectors_per_clu; i++)
+ buf_release(sb, sector+i);
+ }
+
+ prev = clu;
+ if (FAT_read(sb, clu, &clu) == -1)
+ break;
+
+ if (FAT_write(sb, prev, CLUSTER_32(0)) < 0)
+ break;
+ num_clusters++;
+
+ } while (clu != CLUSTER_32(~0));
+
+ if (p_fs->used_clusters != (u32) ~0)
+ p_fs->used_clusters -= num_clusters;
+}
+
+void exfat_free_cluster(struct super_block *sb, struct chain_t *p_chain,
+ s32 do_relse)
+{
+ s32 num_clusters = 0;
+ u32 clu;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ int i;
+ sector_t sector;
+
+ if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0)))
+ return;
+
+ if (p_chain->size <= 0) {
+ pr_err("[EXFAT] free_cluster : skip free-req clu:%u, because of zero-size truncation\n",
+ p_chain->dir);
+ return;
+ }
+
+ __set_sb_dirty(sb);
+ clu = p_chain->dir;
+
+ if (p_chain->flags == 0x03) {
+ do {
+ if (do_relse) {
+ sector = START_SECTOR(clu);
+ for (i = 0; i < p_fs->sectors_per_clu; i++)
+ buf_release(sb, sector+i);
+ }
+
+ if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS)
+ break;
+ clu++;
+
+ num_clusters++;
+ } while (num_clusters < p_chain->size);
+ } else {
+ do {
+ if (p_fs->dev_ejected)
+ break;
+
+ if (do_relse) {
+ sector = START_SECTOR(clu);
+ for (i = 0; i < p_fs->sectors_per_clu; i++)
+ buf_release(sb, sector+i);
+ }
+
+ if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS)
+ break;
+
+ if (FAT_read(sb, clu, &clu) == -1)
+ break;
+ num_clusters++;
+ } while ((clu != CLUSTER_32(0)) && (clu != CLUSTER_32(~0)));
+ }
+
+ if (p_fs->used_clusters != (u32) ~0)
+ p_fs->used_clusters -= num_clusters;
+}
+
+u32 find_last_cluster(struct super_block *sb, struct chain_t *p_chain)
+{
+ u32 clu, next;
+ struct 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;
+}
+
+s32 count_num_clusters(struct super_block *sb, struct chain_t *p_chain)
+{
+ int i, count = 0;
+ u32 clu;
+ struct 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;
+}
+
+s32 fat_count_used_clusters(struct super_block *sb)
+{
+ int i, count = 0;
+ u32 clu;
+ struct 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;
+}
+
+s32 exfat_count_used_clusters(struct super_block *sb)
+{
+ int i, map_i, map_b, count = 0;
+ u8 k;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+
+ map_i = map_b = 0;
+
+ for (i = 2; i < p_fs->num_clusters; i += 8) {
+ k = *(((u8 *) p_fs->vol_amap[map_i]->b_data) + map_b);
+ count += used_bit[k];
+
+ if ((++map_b) >= p_bd->sector_size) {
+ map_i++;
+ map_b = 0;
+ }
+ }
+
+ return count;
+}
+
+void exfat_chain_cont_cluster(struct super_block *sb, u32 chain, s32 len)
+{
+ if (len == 0)
+ return;
+
+ while (len > 1) {
+ if (FAT_write(sb, chain, chain+1) < 0)
+ break;
+ chain++;
+ len--;
+ }
+ FAT_write(sb, chain, CLUSTER_32(~0));
+}
+
+/*
+ * Allocation Bitmap Management Functions
+ */
+
+s32 load_alloc_bitmap(struct super_block *sb)
+{
+ int i, j, ret;
+ u32 map_size;
+ u32 type;
+ sector_t sector;
+ struct chain_t clu;
+ struct bmap_dentry_t *ep;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct 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 = (struct bmap_dentry_t *)get_entry_in_dir(sb, &clu,
+ i, NULL);
+ if (!ep)
+ return FFS_MEDIAERR;
+
+ type = p_fs->fs_func->get_entry_type((struct dentry_t *)ep);
+
+ if (type == TYPE_UNUSED)
+ break;
+ if (type != TYPE_BITMAP)
+ continue;
+
+ if (ep->flags == 0x0) {
+ p_fs->map_clu = GET32_A(ep->start_clu);
+ map_size = (u32) GET64_A(ep->size);
+
+ p_fs->map_sectors = ((map_size-1) >> p_bd->sector_size_bits) + 1;
+
+ p_fs->vol_amap = kmalloc_array(p_fs->map_sectors,
+ sizeof(struct buffer_head *),
+ GFP_KERNEL);
+ if (p_fs->vol_amap == NULL)
+ return FFS_MEMORYERR;
+
+ sector = START_SECTOR(p_fs->map_clu);
+
+ for (j = 0; j < p_fs->map_sectors; j++) {
+ p_fs->vol_amap[j] = NULL;
+ ret = sector_read(sb, sector+j, &(p_fs->vol_amap[j]), 1);
+ if (ret != FFS_SUCCESS) {
+ /* release all buffers and free vol_amap */
+ i = 0;
+ while (i < j)
+ brelse(p_fs->vol_amap[i++]);
+
+ kfree(p_fs->vol_amap);
+ p_fs->vol_amap = NULL;
+ return ret;
+ }
+ }
+
+ p_fs->pbr_bh = NULL;
+ return FFS_SUCCESS;
+ }
+ }
+
+ if (FAT_read(sb, clu.dir, &clu.dir) != 0)
+ return FFS_MEDIAERR;
+ }
+
+ return FFS_FORMATERR;
+}
+
+void free_alloc_bitmap(struct super_block *sb)
+{
+ int i;
+ struct 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]);
+
+ kfree(p_fs->vol_amap);
+ p_fs->vol_amap = NULL;
+}
+
+s32 set_alloc_bitmap(struct super_block *sb, u32 clu)
+{
+ int i, b;
+ sector_t sector;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+
+ i = clu >> (p_bd->sector_size_bits + 3);
+ b = clu & ((p_bd->sector_size << 3) - 1);
+
+ sector = START_SECTOR(p_fs->map_clu) + i;
+
+ exfat_bitmap_set((u8 *) p_fs->vol_amap[i]->b_data, b);
+
+ return sector_write(sb, sector, p_fs->vol_amap[i], 0);
+}
+
+s32 clr_alloc_bitmap(struct super_block *sb, u32 clu)
+{
+ int i, b;
+ sector_t sector;
+#ifdef CONFIG_EXFAT_DISCARD
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ struct exfat_mount_options *opts = &sbi->options;
+ int ret;
+#endif /* CONFIG_EXFAT_DISCARD */
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+
+ i = clu >> (p_bd->sector_size_bits + 3);
+ b = clu & ((p_bd->sector_size << 3) - 1);
+
+ sector = START_SECTOR(p_fs->map_clu) + i;
+
+ exfat_bitmap_clear((u8 *) p_fs->vol_amap[i]->b_data, b);
+
+ return sector_write(sb, sector, p_fs->vol_amap[i], 0);
+
+#ifdef CONFIG_EXFAT_DISCARD
+ if (opts->discard) {
+ ret = sb_issue_discard(sb, START_SECTOR(clu),
+ (1 << p_fs->sectors_per_clu_bits),
+ GFP_NOFS, 0);
+ if (ret == -EOPNOTSUPP) {
+ pr_warn("discard not supported by device, disabling");
+ opts->discard = 0;
+ }
+ }
+#endif /* CONFIG_EXFAT_DISCARD */
+}
+
+u32 test_alloc_bitmap(struct super_block *sb, u32 clu)
+{
+ int i, map_i, map_b;
+ u32 clu_base, clu_free;
+ u8 k, clu_mask;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+
+ clu_base = (clu & ~(0x7)) + 2;
+ clu_mask = (1 << (clu - clu_base + 2)) - 1;
+
+ map_i = clu >> (p_bd->sector_size_bits + 3);
+ map_b = (clu >> 3) & p_bd->sector_size_mask;
+
+ for (i = 2; i < p_fs->num_clusters; i += 8) {
+ k = *(((u8 *) p_fs->vol_amap[map_i]->b_data) + map_b);
+ if (clu_mask > 0) {
+ k |= clu_mask;
+ clu_mask = 0;
+ }
+ if (k < 0xFF) {
+ clu_free = clu_base + free_bit[k];
+ if (clu_free < p_fs->num_clusters)
+ return clu_free;
+ }
+ clu_base += 8;
+
+ if (((++map_b) >= p_bd->sector_size) ||
+ (clu_base >= p_fs->num_clusters)) {
+ if ((++map_i) >= p_fs->map_sectors) {
+ clu_base = 2;
+ map_i = 0;
+ }
+ map_b = 0;
+ }
+ }
+
+ return CLUSTER_32(~0);
+}
+
+void sync_alloc_bitmap(struct super_block *sb)
+{
+ int i;
+ struct 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]);
+}
+
+/*
+ * Upcase table Management Functions
+ */
+static s32 __load_upcase_table(struct super_block *sb, sector_t sector,
+ u32 num_sectors, u32 utbl_checksum)
+{
+ int i, ret = FFS_ERROR;
+ u32 j;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+ struct buffer_head *tmp_bh = NULL;
+ sector_t end_sector = num_sectors + sector;
+
+ u8 skip = FALSE;
+ u32 index = 0;
+ u16 uni = 0;
+ u16 **upcase_table;
+
+ u32 checksum = 0;
+
+ upcase_table = p_fs->vol_utbl = kmalloc(UTBL_COL_COUNT * sizeof(u16 *),
+ GFP_KERNEL);
+ if (upcase_table == NULL)
+ return FFS_MEMORYERR;
+ memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *));
+
+ while (sector < end_sector) {
+ ret = sector_read(sb, sector, &tmp_bh, 1);
+ if (ret != FFS_SUCCESS) {
+ pr_debug("sector read (0x%llX)fail\n",
+ (unsigned long long)sector);
+ goto error;
+ }
+ sector++;
+
+ for (i = 0; i < p_bd->sector_size && index <= 0xFFFF; i += 2) {
+ uni = GET16(((u8 *) tmp_bh->b_data)+i);
+
+ checksum = ((checksum & 1) ? 0x80000000 : 0) +
+ (checksum >> 1) + *(((u8 *)tmp_bh->b_data) +
+ i);
+ checksum = ((checksum & 1) ? 0x80000000 : 0) +
+ (checksum >> 1) + *(((u8 *)tmp_bh->b_data) +
+ (i + 1));
+
+ if (skip) {
+ pr_debug("skip from 0x%X ", index);
+ index += uni;
+ pr_debug("to 0x%X (amount of 0x%X)\n",
+ index, uni);
+ skip = FALSE;
+ } else if (uni == index)
+ index++;
+ else if (uni == 0xFFFF)
+ skip = TRUE;
+ else { /* uni != index , uni != 0xFFFF */
+ u16 col_index = get_col_index(index);
+
+ if (upcase_table[col_index] == NULL) {
+ pr_debug("alloc = 0x%X\n", col_index);
+ upcase_table[col_index] = kmalloc_array(UTBL_ROW_COUNT,
+ sizeof(u16), GFP_KERNEL);
+ if (upcase_table[col_index] == NULL) {
+ ret = FFS_MEMORYERR;
+ goto error;
+ }
+
+ for (j = 0; j < UTBL_ROW_COUNT; j++)
+ upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j;
+ }
+
+ upcase_table[col_index][get_row_index(index)] = uni;
+ index++;
+ }
+ }
+ }
+ if (index >= 0xFFFF && utbl_checksum == checksum) {
+ if (tmp_bh)
+ brelse(tmp_bh);
+ return FFS_SUCCESS;
+ }
+ ret = FFS_ERROR;
+error:
+ if (tmp_bh)
+ brelse(tmp_bh);
+ free_upcase_table(sb);
+ return ret;
+}
+
+static s32 __load_default_upcase_table(struct super_block *sb)
+{
+ int i, ret = FFS_ERROR;
+ u32 j;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ u8 skip = FALSE;
+ u32 index = 0;
+ u16 uni = 0;
+ u16 **upcase_table;
+
+ upcase_table = p_fs->vol_utbl = kmalloc(UTBL_COL_COUNT * sizeof(u16 *),
+ GFP_KERNEL);
+ if (upcase_table == NULL)
+ return FFS_MEMORYERR;
+ memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *));
+
+ for (i = 0; index <= 0xFFFF && i < NUM_UPCASE*2; i += 2) {
+ uni = GET16(uni_upcase + i);
+ if (skip) {
+ pr_debug("skip from 0x%X ", index);
+ index += uni;
+ pr_debug("to 0x%X (amount of 0x%X)\n", index, uni);
+ skip = FALSE;
+ } else if (uni == index)
+ index++;
+ else if (uni == 0xFFFF)
+ skip = TRUE;
+ else { /* uni != index , uni != 0xFFFF */
+ u16 col_index = get_col_index(index);
+
+ if (upcase_table[col_index] == NULL) {
+ pr_debug("alloc = 0x%X\n", col_index);
+ upcase_table[col_index] = kmalloc_array(UTBL_ROW_COUNT,
+ sizeof(u16),
+ GFP_KERNEL);
+ if (upcase_table[col_index] == NULL) {
+ ret = FFS_MEMORYERR;
+ goto error;
+ }
+
+ for (j = 0; j < UTBL_ROW_COUNT; j++)
+ upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j;
+ }
+
+ upcase_table[col_index][get_row_index(index)] = uni;
+ index++;
+ }
+ }
+
+ if (index >= 0xFFFF)
+ return FFS_SUCCESS;
+
+error:
+ /* FATAL error: default upcase table has error */
+ free_upcase_table(sb);
+ return ret;
+}
+
+s32 load_upcase_table(struct super_block *sb)
+{
+ int i;
+ u32 tbl_clu, tbl_size;
+ sector_t sector;
+ u32 type, num_sectors;
+ struct chain_t clu;
+ struct case_dentry_t *ep;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct 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 = (struct case_dentry_t *)get_entry_in_dir(sb, &clu,
+ i, NULL);
+ if (!ep)
+ return FFS_MEDIAERR;
+
+ type = p_fs->fs_func->get_entry_type((struct dentry_t *)ep);
+
+ if (type == TYPE_UNUSED)
+ break;
+ if (type != TYPE_UPCASE)
+ continue;
+
+ tbl_clu = GET32_A(ep->start_clu);
+ tbl_size = (u32) GET64_A(ep->size);
+
+ sector = START_SECTOR(tbl_clu);
+ num_sectors = ((tbl_size-1) >> p_bd->sector_size_bits) + 1;
+ if (__load_upcase_table(sb, sector, num_sectors,
+ GET32_A(ep->checksum)) != FFS_SUCCESS)
+ break;
+ 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);
+}
+
+void free_upcase_table(struct super_block *sb)
+{
+ u32 i;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ u16 **upcase_table;
+
+ upcase_table = p_fs->vol_utbl;
+ for (i = 0; i < UTBL_COL_COUNT; i++)
+ kfree(upcase_table[i]);
+
+ kfree(p_fs->vol_utbl);
+ p_fs->vol_utbl = NULL;
+}
+
+/*
+ * Directory Entry Management Functions
+ */
+
+u32 fat_get_entry_type(struct dentry_t *p_entry)
+{
+ struct dos_dentry_t *ep = (struct 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;
+}
+
+u32 exfat_get_entry_type(struct dentry_t *p_entry)
+{
+ struct file_dentry_t *ep = (struct file_dentry_t *) p_entry;
+
+ if (ep->type == 0x0) {
+ return TYPE_UNUSED;
+ } else if (ep->type < 0x80) {
+ return TYPE_DELETED;
+ } else if (ep->type == 0x80) {
+ return TYPE_INVALID;
+ } else if (ep->type < 0xA0) {
+ if (ep->type == 0x81) {
+ return TYPE_BITMAP;
+ } else if (ep->type == 0x82) {
+ return TYPE_UPCASE;
+ } else if (ep->type == 0x83) {
+ return TYPE_VOLUME;
+ } else if (ep->type == 0x85) {
+ if (GET16_A(ep->attr) & ATTR_SUBDIR)
+ return TYPE_DIR;
+ else
+ return TYPE_FILE;
+ }
+ return TYPE_CRITICAL_PRI;
+ } else if (ep->type < 0xC0) {
+ if (ep->type == 0xA0)
+ return TYPE_GUID;
+ else if (ep->type == 0xA1)
+ return TYPE_PADDING;
+ else if (ep->type == 0xA2)
+ return TYPE_ACLTAB;
+ return TYPE_BENIGN_PRI;
+ } else if (ep->type < 0xE0) {
+ if (ep->type == 0xC0)
+ return TYPE_STREAM;
+ else if (ep->type == 0xC1)
+ return TYPE_EXTEND;
+ else if (ep->type == 0xC2)
+ return TYPE_ACL;
+ return TYPE_CRITICAL_SEC;
+ }
+
+ return TYPE_BENIGN_SEC;
+}
+
+void fat_set_entry_type(struct dentry_t *p_entry, u32 type)
+{
+ struct dos_dentry_t *ep = (struct dos_dentry_t *) p_entry;
+
+ if (type == TYPE_UNUSED)
+ *(ep->name) = 0x0;
+
+ else if (type == TYPE_DELETED)
+ *(ep->name) = 0xE5;
+
+ else if (type == TYPE_EXTEND)
+ ep->attr = ATTR_EXTEND;
+
+ else if (type == TYPE_DIR)
+ ep->attr = ATTR_SUBDIR;
+
+ else if (type == TYPE_FILE)
+ ep->attr = ATTR_ARCHIVE;
+
+ else if (type == TYPE_SYMLINK)
+ ep->attr = ATTR_ARCHIVE | ATTR_SYMLINK;
+}
+
+void exfat_set_entry_type(struct dentry_t *p_entry, u32 type)
+{
+ struct file_dentry_t *ep = (struct 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);
+ }
+}
+
+u32 fat_get_entry_attr(struct dentry_t *p_entry)
+{
+ struct dos_dentry_t *ep = (struct dos_dentry_t *) p_entry;
+
+ return (u32) ep->attr;
+}
+
+u32 exfat_get_entry_attr(struct dentry_t *p_entry)
+{
+ struct file_dentry_t *ep = (struct file_dentry_t *) p_entry;
+
+ return (u32) GET16_A(ep->attr);
+}
+
+void fat_set_entry_attr(struct dentry_t *p_entry, u32 attr)
+{
+ struct dos_dentry_t *ep = (struct dos_dentry_t *) p_entry;
+
+ ep->attr = (u8) attr;
+}
+
+void exfat_set_entry_attr(struct dentry_t *p_entry, u32 attr)
+{
+ struct file_dentry_t *ep = (struct file_dentry_t *) p_entry;
+
+ SET16_A(ep->attr, (u16) attr);
+}
+
+u8 fat_get_entry_flag(struct dentry_t *p_entry)
+{
+ return 0x01;
+}
+
+u8 exfat_get_entry_flag(struct dentry_t *p_entry)
+{
+ struct strm_dentry_t *ep = (struct strm_dentry_t *) p_entry;
+
+ return ep->flags;
+}
+
+void fat_set_entry_flag(struct dentry_t *p_entry, u8 flags)
+{
+}
+
+void exfat_set_entry_flag(struct dentry_t *p_entry, u8 flags)
+{
+ struct strm_dentry_t *ep = (struct strm_dentry_t *) p_entry;
+
+ ep->flags = flags;
+}
+
+u32 fat_get_entry_clu0(struct dentry_t *p_entry)
+{
+ struct dos_dentry_t *ep = (struct dos_dentry_t *) p_entry;
+
+ return ((u32)GET16_A(ep->start_clu_hi) << 16) |
+ GET16_A(ep->start_clu_lo);
+}
+
+u32 exfat_get_entry_clu0(struct dentry_t *p_entry)
+{
+ struct strm_dentry_t *ep = (struct strm_dentry_t *) p_entry;
+
+ return GET32_A(ep->start_clu);
+}
+
+void fat_set_entry_clu0(struct dentry_t *p_entry, u32 start_clu)
+{
+ struct dos_dentry_t *ep = (struct dos_dentry_t *)p_entry;
+
+ SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu));
+ SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16));
+}
+
+void exfat_set_entry_clu0(struct dentry_t *p_entry, u32 start_clu)
+{
+ struct strm_dentry_t *ep = (struct strm_dentry_t *)p_entry;
+
+ SET32_A(ep->start_clu, start_clu);
+}
+
+u64 fat_get_entry_size(struct dentry_t *p_entry)
+{
+ struct dos_dentry_t *ep = (struct dos_dentry_t *)p_entry;
+
+ return (u64) GET32_A(ep->size);
+}
+
+u64 exfat_get_entry_size(struct dentry_t *p_entry)
+{
+ struct strm_dentry_t *ep = (struct strm_dentry_t *)p_entry;
+
+ return GET64_A(ep->valid_size);
+}
+
+void fat_set_entry_size(struct dentry_t *p_entry, u64 size)
+{
+ struct dos_dentry_t *ep = (struct dos_dentry_t *)p_entry;
+
+ SET32_A(ep->size, (u32) size);
+}
+
+void exfat_set_entry_size(struct dentry_t *p_entry, u64 size)
+{
+ struct strm_dentry_t *ep = (struct strm_dentry_t *)p_entry;
+
+ SET64_A(ep->valid_size, size);
+ SET64_A(ep->size, size);
+}
+
+void fat_get_entry_time(struct dentry_t *p_entry, struct timestamp_t *tp,
+ u8 mode)
+{
+ u16 t = 0x00, d = 0x21;
+ struct dos_dentry_t *ep = (struct dos_dentry_t *)p_entry;
+
+ switch (mode) {
+ case TM_CREATE:
+ t = GET16_A(ep->create_time);
+ d = GET16_A(ep->create_date);
+ break;
+ case TM_MODIFY:
+ t = GET16_A(ep->modify_time);
+ d = GET16_A(ep->modify_date);
+ break;
+ }
+
+ tp->sec = (t & 0x001F) << 1;
+ tp->min = (t >> 5) & 0x003F;
+ tp->hour = (t >> 11);
+ tp->day = (d & 0x001F);
+ tp->mon = (d >> 5) & 0x000F;
+ tp->year = (d >> 9);
+}
+
+void exfat_get_entry_time(struct dentry_t *p_entry, struct timestamp_t *tp,
+ u8 mode)
+{
+ u16 t = 0x00, d = 0x21;
+ struct file_dentry_t *ep = (struct file_dentry_t *)p_entry;
+
+ switch (mode) {
+ case TM_CREATE:
+ t = GET16_A(ep->create_time);
+ d = GET16_A(ep->create_date);
+ break;
+ case TM_MODIFY:
+ t = GET16_A(ep->modify_time);
+ d = GET16_A(ep->modify_date);
+ break;
+ case TM_ACCESS:
+ t = GET16_A(ep->access_time);
+ d = GET16_A(ep->access_date);
+ break;
+ }
+
+ tp->sec = (t & 0x001F) << 1;
+ tp->min = (t >> 5) & 0x003F;
+ tp->hour = (t >> 11);
+ tp->day = (d & 0x001F);
+ tp->mon = (d >> 5) & 0x000F;
+ tp->year = (d >> 9);
+}
+
+void fat_set_entry_time(struct dentry_t *p_entry, struct timestamp_t *tp,
+ u8 mode)
+{
+ u16 t, d;
+ struct dos_dentry_t *ep = (struct dos_dentry_t *)p_entry;
+
+ t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1);
+ d = (tp->year << 9) | (tp->mon << 5) | tp->day;
+
+ switch (mode) {
+ case TM_CREATE:
+ SET16_A(ep->create_time, t);
+ SET16_A(ep->create_date, d);
+ break;
+ case TM_MODIFY:
+ SET16_A(ep->modify_time, t);
+ SET16_A(ep->modify_date, d);
+ break;
+ }
+}
+
+void exfat_set_entry_time(struct dentry_t *p_entry, struct timestamp_t *tp,
+ u8 mode)
+{
+ u16 t, d;
+ struct file_dentry_t *ep = (struct 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;
+ }
+}
+
+s32 fat_init_dir_entry(struct super_block *sb, struct chain_t *p_dir, s32 entry,
+ u32 type, u32 start_clu, u64 size)
+{
+ sector_t sector;
+ struct dos_dentry_t *dos_ep;
+
+ dos_ep = (struct 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;
+}
+
+s32 exfat_init_dir_entry(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, u32 type, u32 start_clu, u64 size)
+{
+ sector_t sector;
+ u8 flags;
+ struct file_dentry_t *file_ep;
+ struct 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 = (struct file_dentry_t *)get_entry_in_dir(sb, p_dir, entry,
+ §or);
+ if (!file_ep)
+ return FFS_MEDIAERR;
+
+ strm_ep = (struct 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;
+}
+
+static s32 fat_init_ext_entry(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, s32 num_entries,
+ struct uni_name_t *p_uniname,
+ struct dos_name_t *p_dosname)
+{
+ int i;
+ sector_t sector;
+ u8 chksum;
+ u16 *uniname = p_uniname->name;
+ struct dos_dentry_t *dos_ep;
+ struct ext_dentry_t *ext_ep;
+
+ dos_ep = (struct 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 = (struct 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 = (struct 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;
+}
+
+static s32 exfat_init_ext_entry(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, s32 num_entries,
+ struct uni_name_t *p_uniname,
+ struct dos_name_t *p_dosname)
+{
+ int i;
+ sector_t sector;
+ u16 *uniname = p_uniname->name;
+ struct file_dentry_t *file_ep;
+ struct strm_dentry_t *strm_ep;
+ struct name_dentry_t *name_ep;
+
+ file_ep = (struct file_dentry_t *)get_entry_in_dir(sb, p_dir, entry,
+ §or);
+ if (!file_ep)
+ return FFS_MEDIAERR;
+
+ file_ep->num_ext = (u8)(num_entries - 1);
+ buf_modify(sb, sector);
+
+ strm_ep = (struct 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 = (struct name_dentry_t *)get_entry_in_dir(sb, p_dir,
+ entry + i,
+ §or);
+ if (!name_ep)
+ return FFS_MEDIAERR;
+
+ init_name_entry(name_ep, uniname);
+ buf_modify(sb, sector);
+ uniname += 15;
+ }
+
+ update_dir_checksum(sb, p_dir, entry);
+
+ return FFS_SUCCESS;
+}
+
+void init_dos_entry(struct dos_dentry_t *ep, u32 type, u32 start_clu)
+{
+ struct timestamp_t tm, *tp;
+
+ fat_set_entry_type((struct 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((struct dentry_t *) ep, tp, TM_CREATE);
+ fat_set_entry_time((struct dentry_t *) ep, tp, TM_MODIFY);
+ SET16_A(ep->access_date, 0);
+ ep->create_time_ms = 0;
+}
+
+void init_ext_entry(struct ext_dentry_t *ep, s32 order, u8 chksum, u16 *uniname)
+{
+ int i;
+ u8 end = FALSE;
+
+ fat_set_entry_type((struct dentry_t *) ep, TYPE_EXTEND);
+ ep->order = (u8) order;
+ ep->sysid = 0;
+ ep->checksum = chksum;
+ SET16_A(ep->start_clu, 0);
+
+ for (i = 0; i < 10; i += 2) {
+ if (!end) {
+ SET16(ep->unicode_0_4+i, *uniname);
+ if (*uniname == 0x0)
+ end = TRUE;
+ else
+ uniname++;
+ } else {
+ SET16(ep->unicode_0_4+i, 0xFFFF);
+ }
+ }
+
+ for (i = 0; i < 12; i += 2) {
+ if (!end) {
+ SET16_A(ep->unicode_5_10 + i, *uniname);
+ if (*uniname == 0x0)
+ end = TRUE;
+ else
+ uniname++;
+ } else {
+ SET16_A(ep->unicode_5_10 + i, 0xFFFF);
+ }
+ }
+
+ for (i = 0; i < 4; i += 2) {
+ if (!end) {
+ SET16_A(ep->unicode_11_12 + i, *uniname);
+ if (*uniname == 0x0)
+ end = TRUE;
+ else
+ uniname++;
+ } else {
+ SET16_A(ep->unicode_11_12 + i, 0xFFFF);
+ }
+ }
+}
+
+void init_file_entry(struct file_dentry_t *ep, u32 type)
+{
+ struct timestamp_t tm, *tp;
+
+ exfat_set_entry_type((struct dentry_t *)ep, type);
+
+ tp = tm_current(&tm);
+ exfat_set_entry_time((struct dentry_t *)ep, tp, TM_CREATE);
+ exfat_set_entry_time((struct dentry_t *)ep, tp, TM_MODIFY);
+ exfat_set_entry_time((struct dentry_t *)ep, tp, TM_ACCESS);
+ ep->create_time_ms = 0;
+ ep->modify_time_ms = 0;
+ ep->access_time_ms = 0;
+}
+
+void init_strm_entry(struct strm_dentry_t *ep, u8 flags, u32 start_clu, u64 size)
+{
+ exfat_set_entry_type((struct dentry_t *)ep, TYPE_STREAM);
+ ep->flags = flags;
+ SET32_A(ep->start_clu, start_clu);
+ SET64_A(ep->valid_size, size);
+ SET64_A(ep->size, size);
+}
+
+void init_name_entry(struct name_dentry_t *ep, u16 *uniname)
+{
+ int i;
+
+ exfat_set_entry_type((struct dentry_t *)ep, TYPE_EXTEND);
+ ep->flags = 0x0;
+
+ for (i = 0; i < 30; i++, i++) {
+ SET16_A(ep->unicode_0_14+i, *uniname);
+ if (*uniname == 0x0)
+ break;
+ uniname++;
+ }
+}
+
+void fat_delete_dir_entry(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, s32 order, s32 num_entries)
+{
+ int i;
+ sector_t sector;
+ struct dentry_t *ep;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ for (i = num_entries-1; i >= order; i--) {
+ ep = get_entry_in_dir(sb, p_dir, entry-i, §or);
+ if (!ep)
+ return;
+
+ p_fs->fs_func->set_entry_type(ep, TYPE_DELETED);
+ buf_modify(sb, sector);
+ }
+}
+
+void exfat_delete_dir_entry(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, s32 order, s32 num_entries)
+{
+ int i;
+ sector_t sector;
+ struct dentry_t *ep;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ for (i = order; i < num_entries; i++) {
+ ep = get_entry_in_dir(sb, p_dir, entry+i, §or);
+ if (!ep)
+ return;
+
+ p_fs->fs_func->set_entry_type(ep, TYPE_DELETED);
+ buf_modify(sb, sector);
+ }
+}
+
+void update_dir_checksum(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry)
+{
+ int i, num_entries;
+ sector_t sector;
+ u16 chksum;
+ struct file_dentry_t *file_ep;
+ struct dentry_t *ep;
+
+ file_ep = (struct file_dentry_t *)get_entry_in_dir(sb, p_dir, entry,
+ §or);
+ if (!file_ep)
+ return;
+
+ buf_lock(sb, sector);
+
+ num_entries = (s32) file_ep->num_ext + 1;
+ chksum = calc_checksum_2byte((void *)file_ep, DENTRY_SIZE, 0,
+ CS_DIR_ENTRY);
+
+ for (i = 1; i < num_entries; i++) {
+ ep = get_entry_in_dir(sb, p_dir, entry+i, NULL);
+ if (!ep) {
+ buf_unlock(sb, sector);
+ return;
+ }
+
+ chksum = calc_checksum_2byte((void *)ep, DENTRY_SIZE, chksum,
+ CS_DEFAULT);
+ }
+
+ SET16_A(file_ep->checksum, chksum);
+ buf_modify(sb, sector);
+ buf_unlock(sb, sector);
+}
+
+void update_dir_checksum_with_entry_set(struct super_block *sb,
+ struct entry_set_cache_t *es)
+{
+ struct dentry_t *ep;
+ u16 chksum = 0;
+ s32 chksum_type = CS_DIR_ENTRY, i;
+
+ ep = (struct dentry_t *)&(es->__buf);
+ for (i = 0; i < es->num_entries; i++) {
+ pr_debug("%s ep %p\n", __func__, ep);
+ chksum = calc_checksum_2byte((void *)ep, DENTRY_SIZE, chksum,
+ chksum_type);
+ ep++;
+ chksum_type = CS_DEFAULT;
+ }
+
+ ep = (struct dentry_t *)&(es->__buf);
+ SET16_A(((struct file_dentry_t *)ep)->checksum, chksum);
+ write_whole_entry_set(sb, es);
+}
+
+static s32 _walk_fat_chain(struct super_block *sb, struct chain_t *p_dir,
+ s32 byte_offset, u32 *clu)
+{
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ s32 clu_offset;
+ u32 cur_clu;
+
+ clu_offset = byte_offset >> p_fs->cluster_size_bits;
+ cur_clu = p_dir->dir;
+
+ if (p_dir->flags == 0x03) {
+ cur_clu += clu_offset;
+ } else {
+ while (clu_offset > 0) {
+ if (FAT_read(sb, cur_clu, &cur_clu) == -1)
+ return FFS_MEDIAERR;
+ clu_offset--;
+ }
+ }
+
+ if (clu)
+ *clu = cur_clu;
+ return FFS_SUCCESS;
+}
+
+s32 find_location(struct super_block *sb, struct chain_t *p_dir, s32 entry,
+ sector_t *sector, s32 *offset)
+{
+ s32 off, ret;
+ u32 clu = 0;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct 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;
+
+ /* byte offset in cluster */
+ off &= p_fs->cluster_size - 1;
+
+ /* byte offset in sector */
+ *offset = off & p_bd->sector_size_mask;
+
+ /* sector offset in cluster */
+ *sector = off >> p_bd->sector_size_bits;
+ *sector += START_SECTOR(clu);
+ }
+ return FFS_SUCCESS;
+}
+
+struct dentry_t *get_entry_with_sector(struct super_block *sb, sector_t sector,
+ s32 offset)
+{
+ u8 *buf;
+
+ buf = buf_getblk(sb, sector);
+
+ if (buf == NULL)
+ return NULL;
+
+ return (struct dentry_t *)(buf + offset);
+}
+
+struct dentry_t *get_entry_in_dir(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, sector_t *sector)
+{
+ s32 off;
+ sector_t sec;
+ u8 *buf;
+
+ if (find_location(sb, p_dir, entry, &sec, &off) != FFS_SUCCESS)
+ return NULL;
+
+ buf = buf_getblk(sb, sec);
+
+ if (buf == NULL)
+ return NULL;
+
+ if (sector != NULL)
+ *sector = sec;
+ return (struct dentry_t *)(buf + off);
+}
+
+/* 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
+struct entry_set_cache_t *get_entry_set_in_dir(struct super_block *sb,
+ struct chain_t *p_dir, s32 entry,
+ u32 type,
+ struct dentry_t **file_ep)
+{
+ s32 off, ret, byte_offset;
+ u32 clu = 0;
+ sector_t sec;
+ u32 entry_type;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+ struct entry_set_cache_t *es = NULL;
+ struct dentry_t *ep, *pos;
+ u8 *buf;
+ u8 num_entries;
+ s32 mode = ES_MODE_STARTED;
+ size_t bufsize;
+
+ pr_debug("%s entered p_dir dir %u flags %x size %d\n",
+ __func__, 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 in cluster */
+ byte_offset &= p_fs->cluster_size - 1;
+
+ /* byte offset in sector */
+ off = byte_offset & p_bd->sector_size_mask;
+
+ /* sector offset in cluster */
+ sec = byte_offset >> p_bd->sector_size_bits;
+ sec += START_SECTOR(clu);
+
+ buf = buf_getblk(sb, sec);
+ if (buf == NULL)
+ goto err_out;
+
+
+ ep = (struct 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 = ((struct file_dentry_t *)ep)->num_ext+1;
+ else
+ num_entries = type;
+
+ bufsize = offsetof(struct entry_set_cache_t, __buf) + (num_entries) *
+ sizeof(struct dentry_t);
+ pr_debug("%s: trying to kmalloc %zx bytes for %d entries\n", __func__,
+ bufsize, num_entries);
+ es = kmalloc(bufsize, GFP_KERNEL);
+ if (es == NULL)
+ goto err_out;
+
+ es->num_entries = num_entries;
+ es->sector = sec;
+ es->offset = off;
+ es->alloc_flag = p_dir->flags;
+
+ pos = (struct dentry_t *) &(es->__buf);
+
+ while (num_entries) {
+ /*
+ * instead of copying whole sector, we will check every entry.
+ * this will provide minimum stablity and consistancy.
+ */
+ entry_type = p_fs->fs_func->get_entry_type(ep);
+
+ if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED))
+ goto err_out;
+
+ switch (mode) {
+ case ES_MODE_STARTED:
+ if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR))
+ mode = ES_MODE_GET_FILE_ENTRY;
+ else
+ goto err_out;
+ break;
+ case ES_MODE_GET_FILE_ENTRY:
+ if (entry_type == TYPE_STREAM)
+ mode = ES_MODE_GET_STRM_ENTRY;
+ else
+ goto err_out;
+ break;
+ case ES_MODE_GET_STRM_ENTRY:
+ if (entry_type == TYPE_EXTEND)
+ mode = ES_MODE_GET_NAME_ENTRY;
+ else
+ goto err_out;
+ break;
+ case ES_MODE_GET_NAME_ENTRY:
+ if (entry_type == TYPE_EXTEND)
+ break;
+ else if (entry_type == TYPE_STREAM)
+ goto err_out;
+ else if (entry_type & TYPE_CRITICAL_SEC)
+ mode = ES_MODE_GET_CRITICAL_SEC_ENTRY;
+ else
+ goto err_out;
+ break;
+ case ES_MODE_GET_CRITICAL_SEC_ENTRY:
+ if ((entry_type == TYPE_EXTEND) ||
+ (entry_type == TYPE_STREAM))
+ goto err_out;
+ else if ((entry_type & TYPE_CRITICAL_SEC) !=
+ TYPE_CRITICAL_SEC)
+ goto err_out;
+ break;
+ }
+
+ memcpy(pos, ep, sizeof(struct dentry_t));
+
+ if (--num_entries == 0)
+ break;
+
+ if (((off + DENTRY_SIZE) & p_bd->sector_size_mask) <
+ (off & p_bd->sector_size_mask)) {
+ /* get the next sector */
+ if (IS_LAST_SECTOR_IN_CLUSTER(sec)) {
+ if (es->alloc_flag == 0x03) {
+ clu++;
+ } else {
+ if (FAT_read(sb, clu, &clu) == -1)
+ goto err_out;
+ }
+ sec = START_SECTOR(clu);
+ } else {
+ sec++;
+ }
+ buf = buf_getblk(sb, sec);
+ if (buf == NULL)
+ goto err_out;
+ off = 0;
+ ep = (struct dentry_t *)(buf);
+ } else {
+ ep++;
+ off += DENTRY_SIZE;
+ }
+ pos++;
+ }
+
+ if (file_ep)
+ *file_ep = (struct dentry_t *)&(es->__buf);
+
+ pr_debug("%s exiting es %p sec %llu offset %d flags %d, num_entries %u buf ptr %p\n",
+ __func__, es, (unsigned long long)es->sector, es->offset,
+ es->alloc_flag, es->num_entries, &es->__buf);
+ return es;
+err_out:
+ pr_debug("%s exited NULL (es %p)\n", __func__, es);
+ kfree(es);
+ return NULL;
+}
+
+void release_entry_set(struct entry_set_cache_t *es)
+{
+ pr_debug("%s es=%p\n", __func__, es);
+ kfree(es);
+}
+
+
+static s32 __write_partial_entries_in_entry_set(struct super_block *sb,
+ struct entry_set_cache_t *es,
+ sector_t sec, s32 off, u32 count)
+{
+ s32 num_entries, buf_off = (off - es->offset);
+ u32 remaining_byte_in_sector, copy_entries;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+ u32 clu;
+ u8 *buf, *esbuf = (u8 *)&(es->__buf);
+
+ pr_debug("%s entered es %p sec %llu off %d count %d\n",
+ __func__, es, (unsigned long long)sec, off, count);
+ num_entries = count;
+
+ while (num_entries) {
+ /* white per sector base */
+ remaining_byte_in_sector = (1 << p_bd->sector_size_bits) - off;
+ copy_entries = min_t(s32,
+ remaining_byte_in_sector >> DENTRY_SIZE_BITS,
+ num_entries);
+ buf = buf_getblk(sb, sec);
+ if (buf == NULL)
+ goto err_out;
+ pr_debug("es->buf %p buf_off %u\n", esbuf, buf_off);
+ pr_debug("copying %d entries from %p to sector %llu\n",
+ copy_entries, (esbuf + buf_off),
+ (unsigned long long)sec);
+ memcpy(buf + off, esbuf + buf_off,
+ copy_entries << DENTRY_SIZE_BITS);
+ buf_modify(sb, sec);
+ num_entries -= copy_entries;
+
+ if (num_entries) {
+ /* get next sector */
+ if (IS_LAST_SECTOR_IN_CLUSTER(sec)) {
+ clu = GET_CLUSTER_FROM_SECTOR(sec);
+ if (es->alloc_flag == 0x03) {
+ clu++;
+ } else {
+ if (FAT_read(sb, clu, &clu) == -1)
+ goto err_out;
+ }
+ sec = START_SECTOR(clu);
+ } else {
+ sec++;
+ }
+ off = 0;
+ buf_off += copy_entries << DENTRY_SIZE_BITS;
+ }
+ }
+
+ pr_debug("%s exited successfully\n", __func__);
+ return FFS_SUCCESS;
+err_out:
+ pr_debug("%s failed\n", __func__);
+ return FFS_ERROR;
+}
+
+/* write back all entries in entry set */
+s32 write_whole_entry_set(struct super_block *sb, struct entry_set_cache_t *es)
+{
+ return __write_partial_entries_in_entry_set(sb, es, es->sector,
+ es->offset,
+ es->num_entries);
+}
+
+/* write back some entries in entry set */
+s32 write_partial_entries_in_entry_set(struct super_block *sb,
+ struct entry_set_cache_t *es, struct dentry_t *ep, u32 count)
+{
+ s32 ret, byte_offset, off;
+ u32 clu = 0;
+ sector_t sec;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+ struct chain_t dir;
+
+ /* vaidity check */
+ if (ep + count > ((struct dentry_t *)&(es->__buf)) + es->num_entries)
+ return FFS_ERROR;
+
+ dir.dir = GET_CLUSTER_FROM_SECTOR(es->sector);
+ dir.flags = es->alloc_flag;
+ dir.size = 0xffffffff; /* XXX */
+
+ byte_offset = (es->sector - START_SECTOR(dir.dir)) <<
+ p_bd->sector_size_bits;
+ byte_offset += ((void **)ep - &(es->__buf)) + es->offset;
+
+ ret = _walk_fat_chain(sb, &dir, byte_offset, &clu);
+ if (ret != FFS_SUCCESS)
+ return ret;
+
+ /* byte offset in cluster */
+ byte_offset &= p_fs->cluster_size - 1;
+
+ /* byte offset in sector */
+ off = byte_offset & p_bd->sector_size_mask;
+
+ /* sector offset in cluster */
+ sec = byte_offset >> p_bd->sector_size_bits;
+ sec += START_SECTOR(clu);
+ return __write_partial_entries_in_entry_set(sb, es, sec, off, count);
+}
+
+/* search EMPTY CONTINUOUS "num_entries" entries */
+s32 search_deleted_or_unused_entry(struct super_block *sb,
+ struct chain_t *p_dir, s32 num_entries)
+{
+ int i, dentry, num_empty = 0;
+ s32 dentries_per_clu;
+ u32 type;
+ struct chain_t clu;
+ struct dentry_t *ep;
+ struct 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;
+}
+
+s32 find_empty_entry(struct inode *inode, struct chain_t *p_dir, s32 num_entries)
+{
+ s32 ret, dentry;
+ u32 last_clu;
+ sector_t sector;
+ u64 size = 0;
+ struct chain_t clu;
+ struct dentry_t *ep = NULL;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct file_id_t *fid = &(EXFAT_I(inode)->fid);
+
+ if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
+ return search_deleted_or_unused_entry(sb, p_dir, num_entries);
+
+ while ((dentry = search_deleted_or_unused_entry(sb, p_dir, num_entries)) < 0) {
+ if (p_fs->dev_ejected)
+ break;
+
+ if (p_fs->vol_type == EXFAT) {
+ if (p_dir->dir != p_fs->root_dir)
+ size = i_size_read(inode);
+ }
+
+ last_clu = find_last_cluster(sb, p_dir);
+ clu.dir = last_clu + 1;
+ clu.size = 0;
+ clu.flags = p_dir->flags;
+
+ /* (1) allocate a cluster */
+ ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu);
+ if (ret < 1)
+ return -1;
+
+ if (clear_cluster(sb, clu.dir) != FFS_SUCCESS)
+ return -1;
+
+ /* (2) append to the FAT chain */
+ if (clu.flags != p_dir->flags) {
+ exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size);
+ p_dir->flags = 0x01;
+ p_fs->hint_uentry.clu.flags = 0x01;
+ }
+ if (clu.flags == 0x01)
+ if (FAT_write(sb, last_clu, clu.dir) < 0)
+ return -1;
+
+ if (p_fs->hint_uentry.entry == -1) {
+ p_fs->hint_uentry.dir = p_dir->dir;
+ p_fs->hint_uentry.entry = p_dir->size << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS);
+
+ p_fs->hint_uentry.clu.dir = clu.dir;
+ p_fs->hint_uentry.clu.size = 0;
+ p_fs->hint_uentry.clu.flags = clu.flags;
+ }
+ p_fs->hint_uentry.clu.size++;
+ p_dir->size++;
+
+ /* (3) update the directory entry */
+ if (p_fs->vol_type == EXFAT) {
+ if (p_dir->dir != p_fs->root_dir) {
+ size += p_fs->cluster_size;
+
+ ep = get_entry_in_dir(sb, &fid->dir,
+ fid->entry + 1, §or);
+ if (!ep)
+ return -1;
+ p_fs->fs_func->set_entry_size(ep, size);
+ p_fs->fs_func->set_entry_flag(ep, p_dir->flags);
+ buf_modify(sb, sector);
+
+ update_dir_checksum(sb, &(fid->dir),
+ fid->entry);
+ }
+ }
+
+ i_size_write(inode, i_size_read(inode)+p_fs->cluster_size);
+ EXFAT_I(inode)->mmu_private += p_fs->cluster_size;
+ EXFAT_I(inode)->fid.size += p_fs->cluster_size;
+ EXFAT_I(inode)->fid.flags = p_dir->flags;
+ inode->i_blocks += 1 << (p_fs->cluster_size_bits - 9);
+ }
+
+ return dentry;
+}
+
+/* return values of fat_find_dir_entry()
+ * >= 0 : return dir entiry position with the name in dir
+ * -1 : (root dir, ".") it is the root dir itself
+ * -2 : entry with the name does not exist
+ */
+s32 fat_find_dir_entry(struct super_block *sb, struct chain_t *p_dir,
+ struct uni_name_t *p_uniname, s32 num_entries,
+ struct dos_name_t *p_dosname, u32 type)
+{
+ int i, dentry = 0, lossy = FALSE, len;
+ s32 order = 0, is_feasible_entry = TRUE, has_ext_entry = FALSE;
+ s32 dentries_per_clu;
+ u32 entry_type;
+ u16 entry_uniname[14], *uniname = NULL, unichar;
+ struct chain_t clu;
+ struct dentry_t *ep;
+ struct dos_dentry_t *dos_ep;
+ struct ext_dentry_t *ext_ep;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ if (p_dir->dir == p_fs->root_dir) {
+ if ((!nls_uniname_cmp(sb, p_uniname->name,
+ (u16 *)UNI_CUR_DIR_NAME)) ||
+ (!nls_uniname_cmp(sb, p_uniname->name,
+ (u16 *)UNI_PAR_DIR_NAME)))
+ return -1; // special case, root directory itself
+ }
+
+ if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
+ dentries_per_clu = p_fs->dentries_in_root;
+ else
+ dentries_per_clu = p_fs->dentries_per_clu;
+
+ clu.dir = p_dir->dir;
+ clu.flags = p_dir->flags;
+
+ while (clu.dir != CLUSTER_32(~0)) {
+ if (p_fs->dev_ejected)
+ break;
+
+ for (i = 0; i < dentries_per_clu; i++, dentry++) {
+ ep = get_entry_in_dir(sb, &clu, i, NULL);
+ if (!ep)
+ return -2;
+
+ entry_type = p_fs->fs_func->get_entry_type(ep);
+
+ if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) {
+ if ((type == TYPE_ALL) || (type == entry_type)) {
+ if (is_feasible_entry && has_ext_entry)
+ return dentry;
+
+ dos_ep = (struct dos_dentry_t *) ep;
+ if ((!lossy) && (!nls_dosname_cmp(sb, p_dosname->name, dos_ep->name)))
+ return dentry;
+ }
+ is_feasible_entry = TRUE;
+ has_ext_entry = FALSE;
+ } else if (entry_type == TYPE_EXTEND) {
+ if (is_feasible_entry) {
+ ext_ep = (struct ext_dentry_t *) ep;
+ if (ext_ep->order > 0x40) {
+ order = (s32)(ext_ep->order - 0x40);
+ uniname = p_uniname->name + 13 * (order-1);
+ } else {
+ order = (s32) ext_ep->order;
+ uniname -= 13;
+ }
+
+ len = extract_uni_name_from_ext_entry(ext_ep, entry_uniname, order);
+
+ unichar = *(uniname+len);
+ *(uniname+len) = 0x0;
+
+ if (nls_uniname_cmp(sb, uniname, entry_uniname))
+ is_feasible_entry = FALSE;
+
+ *(uniname+len) = unichar;
+ }
+ has_ext_entry = TRUE;
+ } else if (entry_type == TYPE_UNUSED) {
+ return -2;
+ }
+ 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;
+}
+
+/* return values of exfat_find_dir_entry()
+ * >= 0 : return dir entiry position with the name in dir
+ * -1 : (root dir, ".") it is the root dir itself
+ * -2 : entry with the name does not exist
+ */
+s32 exfat_find_dir_entry(struct super_block *sb, struct chain_t *p_dir,
+ struct uni_name_t *p_uniname, s32 num_entries,
+ struct dos_name_t *p_dosname, u32 type)
+{
+ int i = 0, dentry = 0, num_ext_entries = 0, len, step;
+ s32 order = 0, is_feasible_entry = FALSE;
+ s32 dentries_per_clu, num_empty = 0;
+ u32 entry_type;
+ u16 entry_uniname[16], *uniname = NULL, unichar;
+ struct chain_t clu;
+ struct dentry_t *ep;
+ struct file_dentry_t *file_ep;
+ struct strm_dentry_t *strm_ep;
+ struct name_dentry_t *name_ep;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ if (p_dir->dir == p_fs->root_dir) {
+ if ((!nls_uniname_cmp(sb, p_uniname->name,
+ (u16 *)UNI_CUR_DIR_NAME)) ||
+ (!nls_uniname_cmp(sb, p_uniname->name,
+ (u16 *)UNI_PAR_DIR_NAME)))
+ return -1; // special case, root directory itself
+ }
+
+ if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
+ dentries_per_clu = p_fs->dentries_in_root;
+ else
+ dentries_per_clu = p_fs->dentries_per_clu;
+
+ clu.dir = p_dir->dir;
+ clu.size = p_dir->size;
+ clu.flags = p_dir->flags;
+
+ p_fs->hint_uentry.dir = p_dir->dir;
+ p_fs->hint_uentry.entry = -1;
+
+ while (clu.dir != CLUSTER_32(~0)) {
+ if (p_fs->dev_ejected)
+ break;
+
+ while (i < dentries_per_clu) {
+ ep = get_entry_in_dir(sb, &clu, i, NULL);
+ if (!ep)
+ return -2;
+
+ entry_type = p_fs->fs_func->get_entry_type(ep);
+ step = 1;
+
+ if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) {
+ is_feasible_entry = FALSE;
+
+ if (p_fs->hint_uentry.entry == -1) {
+ num_empty++;
+
+ if (num_empty == 1) {
+ p_fs->hint_uentry.clu.dir = clu.dir;
+ p_fs->hint_uentry.clu.size = clu.size;
+ p_fs->hint_uentry.clu.flags = clu.flags;
+ }
+ if ((num_empty >= num_entries) || (entry_type == TYPE_UNUSED))
+ p_fs->hint_uentry.entry = dentry - (num_empty-1);
+ }
+
+ if (entry_type == TYPE_UNUSED)
+ return -2;
+ } else {
+ num_empty = 0;
+
+ if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) {
+ file_ep = (struct file_dentry_t *) ep;
+ if ((type == TYPE_ALL) || (type == entry_type)) {
+ num_ext_entries = file_ep->num_ext;
+ is_feasible_entry = TRUE;
+ } else {
+ is_feasible_entry = FALSE;
+ step = file_ep->num_ext + 1;
+ }
+ } else if (entry_type == TYPE_STREAM) {
+ if (is_feasible_entry) {
+ strm_ep = (struct strm_dentry_t *)ep;
+ if (p_uniname->name_hash == GET16_A(strm_ep->name_hash) &&
+ p_uniname->name_len == strm_ep->name_len) {
+ order = 1;
+ } else {
+ is_feasible_entry = FALSE;
+ step = num_ext_entries;
+ }
+ }
+ } else if (entry_type == TYPE_EXTEND) {
+ if (is_feasible_entry) {
+ name_ep = (struct name_dentry_t *)ep;
+
+ if ((++order) == 2)
+ uniname = p_uniname->name;
+ else
+ uniname += 15;
+
+ len = extract_uni_name_from_name_entry(name_ep,
+ entry_uniname, order);
+
+ unichar = *(uniname+len);
+ *(uniname+len) = 0x0;
+
+ if (nls_uniname_cmp(sb, uniname, entry_uniname)) {
+ is_feasible_entry = FALSE;
+ step = num_ext_entries - order + 1;
+ } else if (order == num_ext_entries) {
+ p_fs->hint_uentry.dir = CLUSTER_32(~0);
+ p_fs->hint_uentry.entry = -1;
+ return dentry - (num_ext_entries);
+ }
+
+ *(uniname+len) = unichar;
+ }
+ } else {
+ is_feasible_entry = FALSE;
+ }
+ }
+
+ i += step;
+ dentry += step;
+ }
+
+ i -= dentries_per_clu;
+
+ if (p_dir->dir == CLUSTER_32(0))
+ break; /* FAT16 root_dir */
+
+ if (clu.flags == 0x03) {
+ if ((--clu.size) > 0)
+ clu.dir++;
+ else
+ clu.dir = CLUSTER_32(~0);
+ } else {
+ if (FAT_read(sb, clu.dir, &clu.dir) != 0)
+ return -2;
+ }
+ }
+
+ return -2;
+}
+
+s32 fat_count_ext_entries(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, struct dentry_t *p_entry)
+{
+ s32 count = 0;
+ u8 chksum;
+ struct dos_dentry_t *dos_ep = (struct dos_dentry_t *) p_entry;
+ struct ext_dentry_t *ext_ep;
+ struct 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 = (struct 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((struct dentry_t *)ext_ep) ==
+ TYPE_EXTEND) && (ext_ep->checksum == chksum)) {
+ count++;
+ if (ext_ep->order > 0x40)
+ return count;
+ } else {
+ return count;
+ }
+ }
+
+ return count;
+}
+
+s32 exfat_count_ext_entries(struct super_block *sb, struct chain_t *p_dir,
+ s32 entry, struct dentry_t *p_entry)
+{
+ int i, count = 0;
+ u32 type;
+ struct file_dentry_t *file_ep = (struct file_dentry_t *)p_entry;
+ struct dentry_t *ext_ep;
+ struct 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;
+}
+
+s32 count_dos_name_entries(struct super_block *sb, struct chain_t *p_dir,
+ u32 type)
+{
+ int i, count = 0;
+ s32 dentries_per_clu;
+ u32 entry_type;
+ struct chain_t clu;
+ struct dentry_t *ep;
+ struct 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;
+}
+
+bool is_dir_empty(struct super_block *sb, struct chain_t *p_dir)
+{
+ int i, count = 0;
+ s32 dentries_per_clu;
+ u32 type;
+ struct chain_t clu;
+ struct dentry_t *ep;
+ struct 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;
+
+ 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);
+ }
+ if (FAT_read(sb, clu.dir, &clu.dir) != 0)
+ break;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Name Conversion Functions
+ */
+
+/* input : dir, uni_name
+ * output : num_of_entry, dos_name(format : aaaaaa~1.bbb)
+ */
+s32 get_num_entries_and_dos_name(struct super_block *sb, struct chain_t *p_dir,
+ struct uni_name_t *p_uniname, s32 *entries,
+ struct dos_name_t *p_dosname)
+{
+ s32 ret, num_entries;
+ bool lossy = false;
+ char **r;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ num_entries = p_fs->fs_func->calc_num_entries(p_uniname);
+ if (num_entries == 0)
+ return FFS_INVALIDPATH;
+
+ if (p_fs->vol_type != EXFAT) {
+ nls_uniname_to_dosname(sb, p_dosname, p_uniname, &lossy);
+
+ if (lossy) {
+ ret = fat_generate_dos_name(sb, p_dir, p_dosname);
+ if (ret)
+ return ret;
+ } else {
+ for (r = reserved_names; *r; r++) {
+ if (!strncmp((void *)p_dosname->name, *r, 8))
+ return FFS_INVALIDPATH;
+ }
+
+ if (p_dosname->name_case != 0xFF)
+ num_entries = 1;
+ }
+
+ if (num_entries > 1)
+ p_dosname->name_case = 0x0;
+ }
+
+ *entries = num_entries;
+
+ return FFS_SUCCESS;
+}
+
+void get_uni_name_from_dos_entry(struct super_block *sb,
+ struct dos_dentry_t *ep,
+ struct uni_name_t *p_uniname, u8 mode)
+{
+ struct dos_name_t dos_name;
+
+ if (mode == 0x0)
+ dos_name.name_case = 0x0;
+ else
+ dos_name.name_case = ep->lcase;
+
+ memcpy(dos_name.name, ep->name, DOS_NAME_LENGTH);
+ nls_dosname_to_uniname(sb, p_uniname, &dos_name);
+}
+
+void fat_get_uni_name_from_ext_entry(struct super_block *sb,
+ struct chain_t *p_dir, s32 entry,
+ u16 *uniname)
+{
+ int i;
+ struct ext_dentry_t *ep;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ for (entry--, i = 1; entry >= 0; entry--, i++) {
+ ep = (struct ext_dentry_t *)get_entry_in_dir(sb, p_dir, entry,
+ NULL);
+ if (!ep)
+ return;
+
+ if (p_fs->fs_func->get_entry_type((struct dentry_t *)ep) ==
+ TYPE_EXTEND) {
+ extract_uni_name_from_ext_entry(ep, uniname, i);
+ if (ep->order > 0x40)
+ return;
+ } else {
+ return;
+ }
+
+ uniname += 13;
+ }
+}
+
+void exfat_get_uni_name_from_ext_entry(struct super_block *sb,
+ struct chain_t *p_dir, s32 entry,
+ u16 *uniname)
+{
+ int i;
+ struct dentry_t *ep;
+ struct entry_set_cache_t *es;
+ struct 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((struct name_dentry_t *)
+ ep, uniname, i);
+ else
+ goto out;
+ uniname += 15;
+ }
+
+out:
+ release_entry_set(es);
+}
+
+s32 extract_uni_name_from_ext_entry(struct ext_dentry_t *ep, u16 *uniname,
+ s32 order)
+{
+ int i, len = 0;
+
+ for (i = 0; i < 10; i += 2) {
+ *uniname = GET16(ep->unicode_0_4 + i);
+ if (*uniname == 0x0)
+ return len;
+ uniname++;
+ len++;
+ }
+
+ if (order < 20) {
+ for (i = 0; i < 12; i += 2) {
+ *uniname = GET16_A(ep->unicode_5_10 + i);
+ if (*uniname == 0x0)
+ return len;
+ uniname++;
+ len++;
+ }
+ } else {
+ for (i = 0; i < 8; i += 2) {
+ *uniname = GET16_A(ep->unicode_5_10 + i);
+ if (*uniname == 0x0)
+ return len;
+ uniname++;
+ len++;
+ }
+ *uniname = 0x0; /* uniname[MAX_NAME_LENGTH-1] */
+ return len;
+ }
+
+ for (i = 0; i < 4; i += 2) {
+ *uniname = GET16_A(ep->unicode_11_12 + i);
+ if (*uniname == 0x0)
+ return len;
+ uniname++;
+ len++;
+ }
+
+ *uniname = 0x0;
+ return len;
+}
+
+s32 extract_uni_name_from_name_entry(struct name_dentry_t *ep, u16 *uniname,
+ s32 order)
+{
+ int i, len = 0;
+
+ for (i = 0; i < 30; i += 2) {
+ *uniname = GET16_A(ep->unicode_0_14 + i);
+ if (*uniname == 0x0)
+ return len;
+ uniname++;
+ len++;
+ }
+
+ *uniname = 0x0;
+ return len;
+}
+
+s32 fat_generate_dos_name(struct super_block *sb, struct chain_t *p_dir,
+ struct dos_name_t *p_dosname)
+{
+ int i, j, count = 0, count_begin = FALSE;
+ s32 dentries_per_clu;
+ u32 type;
+ u8 bmap[128/* 1 ~ 1023 */];
+ struct chain_t clu;
+ struct dos_dentry_t *ep;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ memset(bmap, 0, sizeof(bmap));
+ exfat_bitmap_set(bmap, 0);
+
+ if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
+ dentries_per_clu = p_fs->dentries_in_root;
+ else
+ dentries_per_clu = p_fs->dentries_per_clu;
+
+ clu.dir = p_dir->dir;
+ clu.flags = p_dir->flags;
+
+ while (clu.dir != CLUSTER_32(~0)) {
+ if (p_fs->dev_ejected)
+ break;
+
+ for (i = 0; i < dentries_per_clu; i++) {
+ ep = (struct dos_dentry_t *)get_entry_in_dir(sb, &clu,
+ i, NULL);
+ if (!ep)
+ return FFS_MEDIAERR;
+
+ type = p_fs->fs_func->get_entry_type((struct dentry_t *)
+ ep);
+
+ if (type == TYPE_UNUSED)
+ break;
+ if ((type != TYPE_FILE) && (type != TYPE_DIR))
+ continue;
+
+ count = 0;
+ count_begin = FALSE;
+
+ for (j = 0; j < 8; j++) {
+ if (ep->name[j] == ' ')
+ break;
+
+ if (ep->name[j] == '~') {
+ count_begin = TRUE;
+ } else if (count_begin) {
+ if ((ep->name[j] >= '0') &&
+ (ep->name[j] <= '9')) {
+ count = count * 10 +
+ (ep->name[j] - '0');
+ } else {
+ count = 0;
+ count_begin = FALSE;
+ }
+ }
+ }
+
+ if ((count > 0) && (count < 1024))
+ exfat_bitmap_set(bmap, count);
+ }
+
+ if (p_dir->dir == CLUSTER_32(0))
+ break; /* FAT16 root_dir */
+
+ if (FAT_read(sb, clu.dir, &clu.dir) != 0)
+ return FFS_MEDIAERR;
+ }
+
+ count = 0;
+ for (i = 0; i < 128; i++) {
+ if (bmap[i] != 0xFF) {
+ for (j = 0; j < 8; j++) {
+ if (exfat_bitmap_test(&bmap[i], j) == 0) {
+ count = (i << 3) + j;
+ break;
+ }
+ }
+ if (count != 0)
+ break;
+ }
+ }
+
+ if ((count == 0) || (count >= 1024))
+ return FFS_FILEEXIST;
+ fat_attach_count_to_dos_name(p_dosname->name, count);
+
+ /* Now dos_name has DOS~????.EXT */
+ return FFS_SUCCESS;
+}
+
+void fat_attach_count_to_dos_name(u8 *dosname, s32 count)
+{
+ int i, j, length;
+ char str_count[6];
+
+ snprintf(str_count, sizeof(str_count), "~%d", count);
+ length = strlen(str_count);
+
+ i = 0;
+ j = 0;
+ while (j <= (8 - length)) {
+ i = j;
+ if (dosname[j] == ' ')
+ break;
+ if (dosname[j] & 0x80)
+ j += 2;
+ else
+ j++;
+ }
+
+ for (j = 0; j < length; i++, j++)
+ dosname[i] = (u8)str_count[j];
+
+ if (i == 7)
+ dosname[7] = ' ';
+}
+
+s32 fat_calc_num_entries(struct uni_name_t *p_uniname)
+{
+ s32 len;
+
+ len = p_uniname->name_len;
+ if (len == 0)
+ return 0;
+
+ /* 1 dos name entry + extended entries */
+ return (len - 1) / 13 + 2;
+}
+
+s32 exfat_calc_num_entries(struct uni_name_t *p_uniname)
+{
+ s32 len;
+
+ len = p_uniname->name_len;
+ if (len == 0)
+ return 0;
+
+ /* 1 file entry + 1 stream entry + name entries */
+ return (len - 1) / 15 + 3;
+}
+
+u8 calc_checksum_1byte(void *data, s32 len, u8 chksum)
+{
+ int i;
+ u8 *c = (u8 *)data;
+
+ for (i = 0; i < len; i++, c++)
+ chksum = (((chksum & 1) << 7) | ((chksum & 0xFE) >> 1)) + *c;
+
+ return chksum;
+}
+
+u16 calc_checksum_2byte(void *data, s32 len, u16 chksum, s32 type)
+{
+ int i;
+ u8 *c = (u8 *)data;
+
+ switch (type) {
+ case CS_DIR_ENTRY:
+ for (i = 0; i < len; i++, c++) {
+ if ((i == 2) || (i == 3))
+ continue;
+ chksum = (((chksum & 1) << 15) |
+ ((chksum & 0xFFFE) >> 1)) + (u16)*c;
+ }
+ break;
+ default
+ :
+ for (i = 0; i < len; i++, c++)
+ chksum = (((chksum & 1) << 15) |
+ ((chksum & 0xFFFE) >> 1)) + (u16)*c;
+ }
+
+ return chksum;
+}
+
+u32 calc_checksum_4byte(void *data, s32 len, u32 chksum, s32 type)
+{
+ int i;
+ u8 *c = (u8 *)data;
+
+ switch (type) {
+ case CS_PBR_SECTOR:
+ for (i = 0; i < len; i++, c++) {
+ if ((i == 106) || (i == 107) || (i == 112))
+ continue;
+ chksum = (((chksum & 1) << 31) |
+ ((chksum & 0xFFFFFFFE) >> 1)) + (u32)*c;
+ }
+ break;
+ default
+ :
+ for (i = 0; i < len; i++, c++)
+ chksum = (((chksum & 1) << 31) |
+ ((chksum & 0xFFFFFFFE) >> 1)) + (u32)*c;
+ }
+
+ return chksum;
+}
+
+/*
+ * Name Resolution Functions
+ */
+
+/* return values of resolve_path()
+ * > 0 : return the length of the path
+ * < 0 : return error
+ */
+s32 resolve_path(struct inode *inode, char *path, struct chain_t *p_dir,
+ struct uni_name_t *p_uniname)
+{
+ bool lossy = false;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct file_id_t *fid = &(EXFAT_I(inode)->fid);
+
+ if (strlen(path) >= (MAX_NAME_LENGTH * MAX_CHARSET_SIZE))
+ return FFS_INVALIDPATH;
+
+ strcpy(name_buf, path);
+
+ nls_cstring_to_uniname(sb, p_uniname, name_buf, &lossy);
+ if (lossy)
+ return FFS_INVALIDPATH;
+
+ fid->size = i_size_read(inode);
+
+ p_dir->dir = fid->start_clu;
+ p_dir->size = (s32)(fid->size >> p_fs->cluster_size_bits);
+ p_dir->flags = fid->flags;
+
+ return FFS_SUCCESS;
+}
+
+/*
+ * File Operation Functions
+ */
+static struct fs_func fat_fs_func = {
+ .alloc_cluster = fat_alloc_cluster,
+ .free_cluster = fat_free_cluster,
+ .count_used_clusters = fat_count_used_clusters,
+
+ .init_dir_entry = fat_init_dir_entry,
+ .init_ext_entry = fat_init_ext_entry,
+ .find_dir_entry = fat_find_dir_entry,
+ .delete_dir_entry = fat_delete_dir_entry,
+ .get_uni_name_from_ext_entry = fat_get_uni_name_from_ext_entry,
+ .count_ext_entries = fat_count_ext_entries,
+ .calc_num_entries = fat_calc_num_entries,
+
+ .get_entry_type = fat_get_entry_type,
+ .set_entry_type = fat_set_entry_type,
+ .get_entry_attr = fat_get_entry_attr,
+ .set_entry_attr = fat_set_entry_attr,
+ .get_entry_flag = fat_get_entry_flag,
+ .set_entry_flag = fat_set_entry_flag,
+ .get_entry_clu0 = fat_get_entry_clu0,
+ .set_entry_clu0 = fat_set_entry_clu0,
+ .get_entry_size = fat_get_entry_size,
+ .set_entry_size = fat_set_entry_size,
+ .get_entry_time = fat_get_entry_time,
+ .set_entry_time = fat_set_entry_time,
+};
+
+s32 fat16_mount(struct super_block *sb, struct pbr_sector_t *p_pbr)
+{
+ s32 num_reserved, num_root_sectors;
+ struct bpb16_t *p_bpb = (struct bpb16_t *)p_pbr->bpb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+
+ if (p_bpb->num_fats == 0)
+ return FFS_FORMATERR;
+
+ num_root_sectors = GET16(p_bpb->num_root_entries) << DENTRY_SIZE_BITS;
+ num_root_sectors = ((num_root_sectors - 1) >>
+ p_bd->sector_size_bits) + 1;
+
+ p_fs->sectors_per_clu = p_bpb->sectors_per_clu;
+ p_fs->sectors_per_clu_bits = ilog2(p_bpb->sectors_per_clu);
+ p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits +
+ p_bd->sector_size_bits;
+ p_fs->cluster_size = 1 << p_fs->cluster_size_bits;
+
+ p_fs->num_FAT_sectors = GET16(p_bpb->num_fat_sectors);
+
+ p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved);
+ if (p_bpb->num_fats == 1)
+ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector;
+ else
+ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector +
+ p_fs->num_FAT_sectors;
+
+ p_fs->root_start_sector = p_fs->FAT2_start_sector +
+ p_fs->num_FAT_sectors;
+ p_fs->data_start_sector = p_fs->root_start_sector + num_root_sectors;
+
+ p_fs->num_sectors = GET16(p_bpb->num_sectors);
+ if (p_fs->num_sectors == 0)
+ p_fs->num_sectors = GET32(p_bpb->num_huge_sectors);
+
+ num_reserved = p_fs->data_start_sector - p_fs->PBR_sector;
+ p_fs->num_clusters = ((p_fs->num_sectors - num_reserved) >>
+ p_fs->sectors_per_clu_bits) + 2;
+ /* because the cluster index starts with 2 */
+
+ if (p_fs->num_clusters < FAT12_THRESHOLD)
+ p_fs->vol_type = FAT12;
+ else
+ p_fs->vol_type = FAT16;
+ p_fs->vol_id = GET32(p_bpb->vol_serial);
+
+ p_fs->root_dir = 0;
+ p_fs->dentries_in_root = GET16(p_bpb->num_root_entries);
+ p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits -
+ DENTRY_SIZE_BITS);
+
+ p_fs->vol_flag = VOL_CLEAN;
+ p_fs->clu_srch_ptr = 2;
+ p_fs->used_clusters = (u32)~0;
+
+ p_fs->fs_func = &fat_fs_func;
+
+ return FFS_SUCCESS;
+}
+
+s32 fat32_mount(struct super_block *sb, struct pbr_sector_t *p_pbr)
+{
+ s32 num_reserved;
+ struct bpb32_t *p_bpb = (struct bpb32_t *)p_pbr->bpb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+
+ if (p_bpb->num_fats == 0)
+ return FFS_FORMATERR;
+
+ p_fs->sectors_per_clu = p_bpb->sectors_per_clu;
+ p_fs->sectors_per_clu_bits = ilog2(p_bpb->sectors_per_clu);
+ p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits +
+ p_bd->sector_size_bits;
+ p_fs->cluster_size = 1 << p_fs->cluster_size_bits;
+
+ p_fs->num_FAT_sectors = GET32(p_bpb->num_fat32_sectors);
+
+ p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved);
+ if (p_bpb->num_fats == 1)
+ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector;
+ else
+ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector +
+ p_fs->num_FAT_sectors;
+
+ p_fs->root_start_sector = p_fs->FAT2_start_sector +
+ p_fs->num_FAT_sectors;
+ p_fs->data_start_sector = p_fs->root_start_sector;
+
+ p_fs->num_sectors = GET32(p_bpb->num_huge_sectors);
+ num_reserved = p_fs->data_start_sector - p_fs->PBR_sector;
+
+ p_fs->num_clusters = ((p_fs->num_sectors - num_reserved) >>
+ p_fs->sectors_per_clu_bits) + 2;
+ /* because the cluster index starts with 2 */
+
+ p_fs->vol_type = FAT32;
+ p_fs->vol_id = GET32(p_bpb->vol_serial);
+
+ p_fs->root_dir = GET32(p_bpb->root_cluster);
+ p_fs->dentries_in_root = 0;
+ p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits -
+ DENTRY_SIZE_BITS);
+
+ p_fs->vol_flag = VOL_CLEAN;
+ p_fs->clu_srch_ptr = 2;
+ p_fs->used_clusters = (u32)~0;
+
+ p_fs->fs_func = &fat_fs_func;
+
+ return FFS_SUCCESS;
+}
+
+static struct fs_func exfat_fs_func = {
+ .alloc_cluster = exfat_alloc_cluster,
+ .free_cluster = exfat_free_cluster,
+ .count_used_clusters = exfat_count_used_clusters,
+
+ .init_dir_entry = exfat_init_dir_entry,
+ .init_ext_entry = exfat_init_ext_entry,
+ .find_dir_entry = exfat_find_dir_entry,
+ .delete_dir_entry = exfat_delete_dir_entry,
+ .get_uni_name_from_ext_entry = exfat_get_uni_name_from_ext_entry,
+ .count_ext_entries = exfat_count_ext_entries,
+ .calc_num_entries = exfat_calc_num_entries,
+
+ .get_entry_type = exfat_get_entry_type,
+ .set_entry_type = exfat_set_entry_type,
+ .get_entry_attr = exfat_get_entry_attr,
+ .set_entry_attr = exfat_set_entry_attr,
+ .get_entry_flag = exfat_get_entry_flag,
+ .set_entry_flag = exfat_set_entry_flag,
+ .get_entry_clu0 = exfat_get_entry_clu0,
+ .set_entry_clu0 = exfat_set_entry_clu0,
+ .get_entry_size = exfat_get_entry_size,
+ .set_entry_size = exfat_set_entry_size,
+ .get_entry_time = exfat_get_entry_time,
+ .set_entry_time = exfat_set_entry_time,
+};
+
+s32 exfat_mount(struct super_block *sb, struct pbr_sector_t *p_pbr)
+{
+ struct bpbex_t *p_bpb = (struct bpbex_t *)p_pbr->bpb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+
+ if (p_bpb->num_fats == 0)
+ return FFS_FORMATERR;
+
+ p_fs->sectors_per_clu = 1 << p_bpb->sectors_per_clu_bits;
+ p_fs->sectors_per_clu_bits = p_bpb->sectors_per_clu_bits;
+ p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits +
+ p_bd->sector_size_bits;
+ p_fs->cluster_size = 1 << p_fs->cluster_size_bits;
+
+ p_fs->num_FAT_sectors = GET32(p_bpb->fat_length);
+
+ p_fs->FAT1_start_sector = p_fs->PBR_sector + GET32(p_bpb->fat_offset);
+ if (p_bpb->num_fats == 1)
+ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector;
+ else
+ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector +
+ p_fs->num_FAT_sectors;
+
+ p_fs->root_start_sector = p_fs->PBR_sector + GET32(p_bpb->clu_offset);
+ p_fs->data_start_sector = p_fs->root_start_sector;
+
+ p_fs->num_sectors = GET64(p_bpb->vol_length);
+ p_fs->num_clusters = GET32(p_bpb->clu_count) + 2;
+ /* because the cluster index starts with 2 */
+
+ p_fs->vol_type = EXFAT;
+ p_fs->vol_id = GET32(p_bpb->vol_serial);
+
+ p_fs->root_dir = GET32(p_bpb->root_cluster);
+ p_fs->dentries_in_root = 0;
+ p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits -
+ DENTRY_SIZE_BITS);
+
+ p_fs->vol_flag = (u32)GET16(p_bpb->vol_flags);
+ p_fs->clu_srch_ptr = 2;
+ p_fs->used_clusters = (u32)~0;
+
+ p_fs->fs_func = &exfat_fs_func;
+
+ return FFS_SUCCESS;
+}
+
+s32 create_dir(struct inode *inode, struct chain_t *p_dir,
+ struct uni_name_t *p_uniname, struct file_id_t *fid)
+{
+ s32 ret, dentry, num_entries;
+ u64 size;
+ struct chain_t clu;
+ struct dos_name_t dos_name, dot_name;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct fs_func *fs_func = p_fs->fs_func;
+
+ 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 = fs_func->alloc_cluster(sb, 1, &clu);
+ if (ret < 0)
+ return FFS_MEDIAERR;
+ else if (ret == 0)
+ return FFS_FULL;
+
+ ret = clear_cluster(sb, clu.dir);
+ if (ret != FFS_SUCCESS)
+ return ret;
+
+ if (p_fs->vol_type == EXFAT) {
+ size = p_fs->cluster_size;
+ } else {
+ size = 0;
+
+ /* initialize the . and .. entry
+ * Information for . points to itself
+ * Information for .. points to parent dir
+ */
+
+ dot_name.name_case = 0x0;
+ memcpy(dot_name.name, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH);
+
+ ret = fs_func->init_dir_entry(sb, &clu, 0, TYPE_DIR, clu.dir,
+ 0);
+ if (ret != FFS_SUCCESS)
+ return ret;
+
+ ret = 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 = fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR,
+ CLUSTER_32(0), 0);
+ else
+ ret = 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 = fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_DIR, clu.dir,
+ size);
+ if (ret != FFS_SUCCESS)
+ return ret;
+
+ ret = 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;
+}
+
+s32 create_file(struct inode *inode, struct chain_t *p_dir,
+ struct uni_name_t *p_uniname, u8 mode, struct file_id_t *fid)
+{
+ s32 ret, dentry, num_entries;
+ struct dos_name_t dos_name;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct fs_func *fs_func = p_fs->fs_func;
+
+ 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 = fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_FILE | mode,
+ CLUSTER_32(0), 0);
+ if (ret != FFS_SUCCESS)
+ return ret;
+
+ ret = fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname,
+ &dos_name);
+ if (ret != FFS_SUCCESS)
+ return ret;
+
+ fid->dir.dir = p_dir->dir;
+ fid->dir.size = p_dir->size;
+ fid->dir.flags = p_dir->flags;
+ fid->entry = dentry;
+
+ fid->attr = ATTR_ARCHIVE | mode;
+ fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01;
+ fid->size = 0;
+ fid->start_clu = CLUSTER_32(~0);
+
+ fid->type = TYPE_FILE;
+ fid->rwoffset = 0;
+ fid->hint_last_off = -1;
+
+ return FFS_SUCCESS;
+}
+
+void remove_file(struct inode *inode, struct chain_t *p_dir, s32 entry)
+{
+ s32 num_entries;
+ sector_t sector;
+ struct dentry_t *ep;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct fs_func *fs_func = p_fs->fs_func;
+
+ 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 = 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 */
+ fs_func->delete_dir_entry(sb, p_dir, entry, 0, num_entries);
+}
+
+s32 rename_file(struct inode *inode, struct chain_t *p_dir, s32 oldentry,
+ struct uni_name_t *p_uniname, struct file_id_t *fid)
+{
+ s32 ret, newentry = -1, num_old_entries, num_new_entries;
+ sector_t sector_old, sector_new;
+ struct dos_name_t dos_name;
+ struct dentry_t *epold, *epnew;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct fs_func *fs_func = p_fs->fs_func;
+
+ 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 = 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 (fs_func->get_entry_type(epnew) == TYPE_FILE) {
+ fs_func->set_entry_attr(epnew,
+ 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 = fs_func->init_ext_entry(sb, p_dir, newentry,
+ num_new_entries, p_uniname,
+ &dos_name);
+ if (ret != FFS_SUCCESS)
+ return ret;
+
+ fs_func->delete_dir_entry(sb, p_dir, oldentry, 0,
+ num_old_entries);
+ fid->entry = newentry;
+ } else {
+ if (fs_func->get_entry_type(epold) == TYPE_FILE) {
+ fs_func->set_entry_attr(epold,
+ fs_func->get_entry_attr(epold) |
+ ATTR_ARCHIVE);
+ fid->attr |= ATTR_ARCHIVE;
+ }
+ buf_modify(sb, sector_old);
+ buf_unlock(sb, sector_old);
+
+ ret = fs_func->init_ext_entry(sb, p_dir, oldentry,
+ num_new_entries, p_uniname,
+ &dos_name);
+ if (ret != FFS_SUCCESS)
+ return ret;
+
+ fs_func->delete_dir_entry(sb, p_dir, oldentry, num_new_entries,
+ num_old_entries);
+ }
+
+ return FFS_SUCCESS;
+}
+
+s32 move_file(struct inode *inode, struct chain_t *p_olddir, s32 oldentry,
+ struct chain_t *p_newdir, struct uni_name_t *p_uniname,
+ struct file_id_t *fid)
+{
+ s32 ret, newentry, num_new_entries, num_old_entries;
+ sector_t sector_mov, sector_new;
+ struct chain_t clu;
+ struct dos_name_t dos_name;
+ struct dentry_t *epmov, *epnew;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct fs_func *fs_func = p_fs->fs_func;
+
+ 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 (fs_func->get_entry_type(epmov) == TYPE_DIR &&
+ 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 = 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 (fs_func->get_entry_type(epnew) == TYPE_FILE) {
+ fs_func->set_entry_attr(epnew, 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 (fs_func->get_entry_type(epnew) == TYPE_DIR) {
+ /* change ".." pointer to new parent dir */
+ clu.dir = 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)
+ fs_func->set_entry_clu0(epnew, CLUSTER_32(0));
+ else
+ fs_func->set_entry_clu0(epnew, p_newdir->dir);
+ buf_modify(sb, sector_new);
+ }
+
+ ret = fs_func->init_ext_entry(sb, p_newdir, newentry, num_new_entries,
+ p_uniname, &dos_name);
+ if (ret != FFS_SUCCESS)
+ return ret;
+
+ 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;
+}
+
+/*
+ * Sector Read/Write Functions
+ */
+
+int sector_read(struct super_block *sb, sector_t sec, struct buffer_head **bh,
+ bool read)
+{
+ s32 ret = FFS_MEDIAERR;
+ struct 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)) {
+ pr_err("[EXFAT] %s: out of range error! (sec = %llu)\n",
+ __func__, (unsigned long long)sec);
+ fs_error(sb);
+ return ret;
+ }
+
+ if (!p_fs->dev_ejected) {
+ ret = bdev_read(sb, sec, bh, 1, read);
+ if (ret != FFS_SUCCESS)
+ p_fs->dev_ejected = TRUE;
+ }
+
+ return ret;
+}
+
+int sector_write(struct super_block *sb, sector_t sec, struct buffer_head *bh,
+ bool sync)
+{
+ s32 ret = FFS_MEDIAERR;
+ struct 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)) {
+ pr_err("[EXFAT] %s: out of range error! (sec = %llu)\n",
+ __func__, (unsigned long long)sec);
+ fs_error(sb);
+ return ret;
+ }
+
+ if (!bh) {
+ pr_err("[EXFAT] %s: bh is NULL!\n", __func__);
+ 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;
+}
+
+int multi_sector_read(struct super_block *sb, sector_t sec,
+ struct buffer_head **bh, s32 num_secs, bool read)
+{
+ s32 ret = FFS_MEDIAERR;
+ struct 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)) {
+ pr_err("[EXFAT] %s: out of range error! (sec = %llu, num_secs = %d)\n",
+ __func__, (unsigned long long)sec, num_secs);
+ fs_error(sb);
+ return ret;
+ }
+
+ if (!p_fs->dev_ejected) {
+ ret = bdev_read(sb, sec, bh, num_secs, read);
+ if (ret != FFS_SUCCESS)
+ p_fs->dev_ejected = TRUE;
+ }
+
+ return ret;
+}
+
+int multi_sector_write(struct super_block *sb, sector_t sec,
+ struct buffer_head *bh, s32 num_secs, bool sync)
+{
+ s32 ret = FFS_MEDIAERR;
+ struct 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)) {
+ pr_err("[EXFAT] %s: out of range error! (sec = %llu, num_secs = %d)\n",
+ __func__, (unsigned long long)sec, num_secs);
+ fs_error(sb);
+ return ret;
+ }
+ if (!bh) {
+ pr_err("[EXFAT] %s: bh is NULL!\n", __func__);
+ fs_error(sb);
+ return ret;
+ }
+
+ if (!p_fs->dev_ejected) {
+ ret = bdev_write(sb, sec, bh, num_secs, sync);
+ if (ret != FFS_SUCCESS)
+ p_fs->dev_ejected = TRUE;
+ }
+
+ return ret;
+}
diff --git a/drivers/staging/exfat/exfat_nls.c b/drivers/staging/exfat/exfat_nls.c
new file mode 100644
index 000000000000..2ca58616159b
--- /dev/null
+++ b/drivers/staging/exfat/exfat_nls.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/string.h>
+#include <linux/nls.h>
+#include "exfat.h"
+
+static u16 bad_dos_chars[] = {
+ /* + , ; = [ ] */
+ 0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D,
+ 0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D,
+ 0
+};
+
+static u16 bad_uni_chars[] = {
+ /* " * / : < > ? \ | */
+ 0x0022, 0x002A, 0x002F, 0x003A,
+ 0x003C, 0x003E, 0x003F, 0x005C, 0x007C,
+ 0
+};
+
+static int convert_ch_to_uni(struct nls_table *nls, u16 *uni, u8 *ch,
+ bool *lossy)
+{
+ int len;
+
+ *uni = 0x0;
+
+ if (ch[0] < 0x80) {
+ *uni = (u16)ch[0];
+ return 1;
+ }
+
+ len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni);
+ if (len < 0) {
+ /* conversion failed */
+ pr_info("%s: fail to use nls\n", __func__);
+ if (lossy)
+ *lossy = true;
+ *uni = (u16)'_';
+ if (!strcmp(nls->charset, "utf8"))
+ return 1;
+ else
+ return 2;
+ }
+
+ return len;
+}
+
+static int convert_uni_to_ch(struct nls_table *nls, u8 *ch, u16 uni,
+ bool *lossy)
+{
+ int len;
+
+ ch[0] = 0x0;
+
+ if (uni < 0x0080) {
+ ch[0] = (u8)uni;
+ return 1;
+ }
+
+ len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE);
+ if (len < 0) {
+ /* conversion failed */
+ pr_info("%s: fail to use nls\n", __func__);
+ if (lossy)
+ *lossy = true;
+ ch[0] = '_';
+ return 1;
+ }
+
+ return len;
+}
+
+u16 nls_upper(struct super_block *sb, u16 a)
+{
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ if (EXFAT_SB(sb)->options.casesensitive)
+ return a;
+ if (p_fs->vol_utbl && p_fs->vol_utbl[get_col_index(a)])
+ return p_fs->vol_utbl[get_col_index(a)][get_row_index(a)];
+ else
+ return a;
+}
+
+static u16 *nls_wstrchr(u16 *str, u16 wchar)
+{
+ while (*str) {
+ if (*(str++) == wchar)
+ return str;
+ }
+
+ return NULL;
+}
+
+int nls_dosname_cmp(struct super_block *sb, u8 *a, u8 *b)
+{
+ return strncmp(a, b, DOS_NAME_LENGTH);
+}
+
+int nls_uniname_cmp(struct super_block *sb, u16 *a, u16 *b)
+{
+ int i;
+
+ for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) {
+ if (nls_upper(sb, *a) != nls_upper(sb, *b))
+ return 1;
+ if (*a == 0x0)
+ return 0;
+ }
+ return 0;
+}
+
+void nls_uniname_to_dosname(struct super_block *sb,
+ struct dos_name_t *p_dosname,
+ struct uni_name_t *p_uniname, bool *p_lossy)
+{
+ int i, j, len;
+ bool lossy = false;
+ u8 buf[MAX_CHARSET_SIZE];
+ u8 lower = 0, upper = 0;
+ u8 *dosname = p_dosname->name;
+ u16 *uniname = p_uniname->name;
+ u16 *p, *last_period;
+ struct nls_table *nls = EXFAT_SB(sb)->nls_disk;
+
+ for (i = 0; i < DOS_NAME_LENGTH; i++)
+ *(dosname + i) = ' ';
+
+ if (!nls_uniname_cmp(sb, uniname, (u16 *)UNI_CUR_DIR_NAME)) {
+ *(dosname) = '.';
+ p_dosname->name_case = 0x0;
+ if (p_lossy)
+ *p_lossy = false;
+ return;
+ }
+
+ if (!nls_uniname_cmp(sb, uniname, (u16 *)UNI_PAR_DIR_NAME)) {
+ *(dosname) = '.';
+ *(dosname + 1) = '.';
+ p_dosname->name_case = 0x0;
+ if (p_lossy)
+ *p_lossy = false;
+ return;
+ }
+
+ /* search for the last embedded period */
+ last_period = NULL;
+ for (p = uniname; *p; p++) {
+ if (*p == (u16)'.')
+ last_period = p;
+ }
+
+ i = 0;
+ while (i < DOS_NAME_LENGTH) {
+ if (i == 8) {
+ if (!last_period)
+ break;
+
+ if (uniname <= last_period) {
+ if (uniname < last_period)
+ lossy = true;
+ uniname = last_period + 1;
+ }
+ }
+
+ if (*uniname == (u16)'\0') {
+ break;
+ } else if (*uniname == (u16)' ') {
+ lossy = true;
+ } else if (*uniname == (u16)'.') {
+ if (uniname < last_period)
+ lossy = true;
+ else
+ i = 8;
+ } else if (nls_wstrchr(bad_dos_chars, *uniname)) {
+ lossy = true;
+ *(dosname + i) = '_';
+ i++;
+ } else {
+ len = convert_uni_to_ch(nls, buf, *uniname, &lossy);
+
+ if (len > 1) {
+ if ((i >= 8) && ((i + len) > DOS_NAME_LENGTH))
+ break;
+
+ if ((i < 8) && ((i + len) > 8)) {
+ i = 8;
+ continue;
+ }
+
+ lower = 0xFF;
+
+ for (j = 0; j < len; j++, i++)
+ *(dosname + i) = *(buf + j);
+ } else { /* len == 1 */
+ if ((*buf >= 'a') && (*buf <= 'z')) {
+ *(dosname + i) = *buf - ('a' - 'A');
+
+ if (i < 8)
+ lower |= 0x08;
+ else
+ lower |= 0x10;
+ } else if ((*buf >= 'A') && (*buf <= 'Z')) {
+ *(dosname + i) = *buf;
+
+ if (i < 8)
+ upper |= 0x08;
+ else
+ upper |= 0x10;
+ } else {
+ *(dosname + i) = *buf;
+ }
+ i++;
+ }
+ }
+
+ uniname++;
+ }
+
+ if (*dosname == 0xE5)
+ *dosname = 0x05;
+
+ if (*uniname != 0x0)
+ lossy = TRUE;
+
+ if (upper & lower)
+ p_dosname->name_case = 0xFF;
+ else
+ p_dosname->name_case = lower;
+
+ if (p_lossy)
+ *p_lossy = lossy;
+}
+
+void nls_dosname_to_uniname(struct super_block *sb,
+ struct uni_name_t *p_uniname,
+ struct dos_name_t *p_dosname)
+{
+ int i = 0, j, n = 0;
+ u8 buf[DOS_NAME_LENGTH + 2];
+ u8 *dosname = p_dosname->name;
+ u16 *uniname = p_uniname->name;
+ struct nls_table *nls = EXFAT_SB(sb)->nls_disk;
+
+ if (*dosname == 0x05) {
+ *buf = 0xE5;
+ i++;
+ n++;
+ }
+
+ for (; i < 8; i++, n++) {
+ if (*(dosname + i) == ' ')
+ break;
+
+ if ((*(dosname + i) >= 'A') && (*(dosname + i) <= 'Z') &&
+ (p_dosname->name_case & 0x08))
+ *(buf + n) = *(dosname + i) + ('a' - 'A');
+ else
+ *(buf + n) = *(dosname + i);
+ }
+ if (*(dosname + 8) != ' ') {
+ *(buf + n) = '.';
+ n++;
+ }
+
+ for (i = 8; i < DOS_NAME_LENGTH; i++, n++) {
+ if (*(dosname + i) == ' ')
+ break;
+
+ if ((*(dosname + i) >= 'A') && (*(dosname + i) <= 'Z') &&
+ (p_dosname->name_case & 0x10))
+ *(buf + n) = *(dosname + i) + ('a' - 'A');
+ else
+ *(buf + n) = *(dosname + i);
+ }
+ *(buf + n) = '\0';
+
+ i = 0;
+ j = 0;
+ while (j < (MAX_NAME_LENGTH - 1)) {
+ if (*(buf + i) == '\0')
+ break;
+
+ i += convert_ch_to_uni(nls, uniname, (buf + i), NULL);
+
+ uniname++;
+ j++;
+ }
+
+ *uniname = (u16)'\0';
+}
+
+void nls_uniname_to_cstring(struct super_block *sb, u8 *p_cstring,
+ struct uni_name_t *p_uniname)
+{
+ int i, j, len;
+ u8 buf[MAX_CHARSET_SIZE];
+ u16 *uniname = p_uniname->name;
+ struct nls_table *nls = EXFAT_SB(sb)->nls_io;
+
+ if (!nls) {
+ len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH,
+ UTF16_HOST_ENDIAN, p_cstring,
+ MAX_NAME_LENGTH);
+ p_cstring[len] = 0;
+ return;
+ }
+
+ i = 0;
+ while (i < (MAX_NAME_LENGTH - 1)) {
+ if (*uniname == (u16)'\0')
+ break;
+
+ len = convert_uni_to_ch(nls, buf, *uniname, NULL);
+
+ if (len > 1) {
+ for (j = 0; j < len; j++)
+ *p_cstring++ = (char)*(buf + j);
+ } else { /* len == 1 */
+ *p_cstring++ = (char)*buf;
+ }
+
+ uniname++;
+ i++;
+ }
+
+ *p_cstring = '\0';
+}
+
+void nls_cstring_to_uniname(struct super_block *sb,
+ struct uni_name_t *p_uniname, u8 *p_cstring,
+ bool *p_lossy)
+{
+ int i, j;
+ bool lossy = false;
+ u8 *end_of_name;
+ u8 upname[MAX_NAME_LENGTH * 2];
+ u16 *uniname = p_uniname->name;
+ struct nls_table *nls = EXFAT_SB(sb)->nls_io;
+
+ /* strip all trailing spaces */
+ end_of_name = p_cstring + strlen(p_cstring);
+
+ while (*(--end_of_name) == ' ') {
+ if (end_of_name < p_cstring)
+ break;
+ }
+ *(++end_of_name) = '\0';
+
+ if (strcmp(p_cstring, ".") && strcmp(p_cstring, "..")) {
+ /* strip all trailing periods */
+ while (*(--end_of_name) == '.') {
+ if (end_of_name < p_cstring)
+ break;
+ }
+ *(++end_of_name) = '\0';
+ }
+
+ if (*p_cstring == '\0')
+ lossy = true;
+
+ if (!nls) {
+ i = utf8s_to_utf16s(p_cstring, MAX_NAME_LENGTH,
+ UTF16_HOST_ENDIAN, uniname,
+ MAX_NAME_LENGTH);
+ for (j = 0; j < i; j++)
+ SET16_A(upname + j * 2, nls_upper(sb, uniname[j]));
+ uniname[i] = '\0';
+ } else {
+ i = 0;
+ j = 0;
+ while (j < (MAX_NAME_LENGTH - 1)) {
+ if (*(p_cstring + i) == '\0')
+ break;
+
+ i += convert_ch_to_uni(nls, uniname,
+ (u8 *)(p_cstring + i), &lossy);
+
+ if ((*uniname < 0x0020) ||
+ nls_wstrchr(bad_uni_chars, *uniname))
+ lossy = true;
+
+ SET16_A(upname + j * 2, nls_upper(sb, *uniname));
+
+ uniname++;
+ j++;
+ }
+
+ if (*(p_cstring + i) != '\0')
+ lossy = true;
+ *uniname = (u16)'\0';
+ }
+
+ p_uniname->name_len = j;
+ p_uniname->name_hash = calc_checksum_2byte(upname, j << 1, 0,
+ CS_DEFAULT);
+
+ if (p_lossy)
+ *p_lossy = lossy;
+}
diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c
new file mode 100644
index 000000000000..5b5c2ca8c9aa
--- /dev/null
+++ b/drivers/staging/exfat/exfat_super.c
@@ -0,0 +1,4137 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/pagemap.h>
+#include <linux/mpage.h>
+#include <linux/buffer_head.h>
+#include <linux/exportfs.h>
+#include <linux/mount.h>
+#include <linux/vfs.h>
+#include <linux/aio.h>
+#include <linux/iversion.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 <linux/time.h>
+
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/mutex.h>
+#include <linux/swap.h>
+
+#define EXFAT_VERSION "1.3.0"
+
+#include "exfat.h"
+
+static struct kmem_cache *exfat_inode_cachep;
+
+// FIXME use commented lines
+// static int exfat_default_codepage = CONFIG_EXFAT_DEFAULT_CODEPAGE;
+// static char exfat_default_iocharset[] = CONFIG_EXFAT_DEFAULT_IOCHARSET;
+static int exfat_default_codepage = CONFIG_FAT_DEFAULT_CODEPAGE;
+static char exfat_default_iocharset[] = CONFIG_FAT_DEFAULT_IOCHARSET;
+
+#define INC_IVERSION(x) (inode_inc_iversion(x))
+#define GET_IVERSION(x) (inode_peek_iversion_raw(x))
+#define SET_IVERSION(x, y) (inode_set_iversion(x, y))
+
+static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos);
+static int exfat_sync_inode(struct inode *inode);
+static struct inode *exfat_build_inode(struct super_block *sb,
+ struct file_id_t *fid, 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);
+
+#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,
+};
+
+/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
+static void exfat_time_fat2unix(struct exfat_sb_info *sbi,
+ struct timespec64 *ts, struct 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 +
+ (ld + accum_days_in_year[(tp->Month)] +
+ (tp->Day - 1)) * SECS_PER_DAY +
+ (year * 365 + 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. */
+static void exfat_time_unix2fat(struct exfat_sb_info *sbi,
+ struct timespec64 *ts, struct 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;
+}
+
+struct timestamp_t *tm_current(struct timestamp_t *tp)
+{
+ struct timespec64 ts;
+ time_t second, day, leap_day, month, year;
+
+ ktime_get_real_ts64(&ts);
+
+ second = ts.tv_sec;
+ second -= sys_tz.tz_minuteswest * SECS_PER_MIN;
+
+ /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
+ if (second < UNIX_SECS_1980) {
+ tp->sec = 0;
+ tp->min = 0;
+ tp->hour = 0;
+ tp->day = 1;
+ tp->mon = 1;
+ tp->year = 0;
+ return tp;
+ }
+#if BITS_PER_LONG == 64
+ if (second >= UNIX_SECS_2108) {
+ tp->sec = 59;
+ tp->min = 59;
+ tp->hour = 23;
+ tp->day = 31;
+ tp->mon = 12;
+ tp->year = 127;
+ return tp;
+ }
+#endif
+
+ day = second / SECS_PER_DAY - DAYS_DELTA_DECADE;
+ year = day / 365;
+
+ MAKE_LEAP_YEAR(leap_day, year);
+ if (year * 365 + leap_day > day)
+ year--;
+
+ MAKE_LEAP_YEAR(leap_day, year);
+
+ day -= year * 365 + leap_day;
+
+ if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) {
+ month = 2;
+ } else {
+ if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3])
+ day--;
+ for (month = 1; month < 12; month++) {
+ if (accum_days_in_year[month + 1] > day)
+ break;
+ }
+ }
+ day -= accum_days_in_year[month];
+
+ tp->sec = second % SECS_PER_MIN;
+ tp->min = (second / SECS_PER_MIN) % 60;
+ tp->hour = (second / SECS_PER_HOUR) % 24;
+ tp->day = day + 1;
+ tp->mon = month;
+ tp->year = year;
+
+ return tp;
+}
+
+static void __lock_super(struct super_block *sb)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+ mutex_lock(&sbi->s_lock);
+}
+
+static void __unlock_super(struct super_block *sb)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+ mutex_unlock(&sbi->s_lock);
+}
+
+static int __is_sb_dirty(struct super_block *sb)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+ return sbi->s_dirt;
+}
+
+static void __set_sb_clean(struct super_block *sb)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+ sbi->s_dirt = 0;
+}
+
+static int __exfat_revalidate(struct dentry *dentry)
+{
+ return 0;
+}
+
+static int exfat_revalidate(struct dentry *dentry, unsigned int flags)
+{
+ if (flags & LOOKUP_RCU)
+ return -ECHILD;
+
+ if (dentry->d_inode)
+ return 1;
+ return __exfat_revalidate(dentry);
+}
+
+static int exfat_revalidate_ci(struct dentry *dentry, unsigned int flags)
+{
+ if (flags & LOOKUP_RCU)
+ return -ECHILD;
+
+ if (dentry->d_inode)
+ return 1;
+
+ if (!flags)
+ return 0;
+
+ if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
+ return 0;
+
+ return __exfat_revalidate(dentry);
+}
+
+static unsigned int __exfat_striptail_len(unsigned int len, const char *name)
+{
+ while (len && name[len - 1] == '.')
+ len--;
+ return len;
+}
+
+static unsigned int exfat_striptail_len(const struct qstr *qstr)
+{
+ return __exfat_striptail_len(qstr->len, qstr->name);
+}
+
+static int exfat_d_hash(const struct dentry *dentry, struct qstr *qstr)
+{
+ qstr->hash = full_name_hash(dentry, qstr->name,
+ exfat_striptail_len(qstr));
+ return 0;
+}
+
+static int exfat_d_hashi(const struct dentry *dentry, struct qstr *qstr)
+{
+ struct super_block *sb = dentry->d_sb;
+ const unsigned char *name;
+ unsigned int len;
+ unsigned long hash;
+
+ name = qstr->name;
+ len = exfat_striptail_len(qstr);
+
+ hash = init_name_hash(dentry);
+ while (len--)
+ hash = partial_name_hash(nls_upper(sb, *name++), hash);
+ qstr->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+static int exfat_cmpi(const struct dentry *dentry, unsigned int len,
+ const char *str, const struct qstr *name)
+{
+ struct nls_table *t = EXFAT_SB(dentry->d_sb)->nls_io;
+ unsigned int alen, blen;
+
+ alen = exfat_striptail_len(name);
+ blen = __exfat_striptail_len(len, str);
+ if (alen == blen) {
+ if (t == NULL) {
+ if (strncasecmp(name->name, str, alen) == 0)
+ return 0;
+ } else if (nls_strnicmp(t, name->name, str, alen) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+static int exfat_cmp(const struct dentry *dentry, unsigned int len,
+ const char *str, const struct qstr *name)
+{
+ unsigned int alen, blen;
+
+ alen = exfat_striptail_len(name);
+ blen = __exfat_striptail_len(len, str);
+ if (alen == blen) {
+ if (strncmp(name->name, str, alen) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+static const struct dentry_operations exfat_ci_dentry_ops = {
+ .d_revalidate = exfat_revalidate_ci,
+ .d_hash = exfat_d_hashi,
+ .d_compare = exfat_cmpi,
+};
+
+static const struct dentry_operations exfat_dentry_ops = {
+ .d_revalidate = exfat_revalidate,
+ .d_hash = exfat_d_hash,
+ .d_compare = exfat_cmp,
+};
+
+static DEFINE_SEMAPHORE(z_sem);
+
+static inline void fs_sync(struct super_block *sb, bool do_sync)
+{
+ if (do_sync)
+ bdev_sync(sb);
+}
+
+/*
+ * 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) & 0222)
+ 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 &= ~0222;
+
+ 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 & 0222))
+ 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);
+}
+
+static int ffsMountVol(struct super_block *sb)
+{
+ int i, ret;
+ struct pbr_sector_t *p_pbr;
+ struct buffer_head *tmp_bh = NULL;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+
+ pr_info("[EXFAT] trying to mount...\n");
+
+ down(&z_sem);
+
+ buf_init(sb);
+
+ sema_init(&p_fs->v_sem, 1);
+ p_fs->dev_ejected = FALSE;
+
+ /* open the block device */
+ bdev_open(sb);
+
+ if (p_bd->sector_size < sb->s_blocksize) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ 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) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+
+ p_fs->PBR_sector = 0;
+
+ p_pbr = (struct 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);
+ ret = FFS_FORMATERR;
+ goto out;
+ }
+
+ /* 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);
+ goto out;
+ }
+
+ if (p_fs->vol_type == EXFAT) {
+ ret = load_alloc_bitmap(sb);
+ if (ret) {
+ bdev_close(sb);
+ goto out;
+ }
+ ret = load_upcase_table(sb);
+ if (ret) {
+ free_alloc_bitmap(sb);
+ bdev_close(sb);
+ goto out;
+ }
+ }
+
+ if (p_fs->dev_ejected) {
+ if (p_fs->vol_type == EXFAT) {
+ free_upcase_table(sb);
+ free_alloc_bitmap(sb);
+ }
+ bdev_close(sb);
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+
+ pr_info("[EXFAT] mounted successfully\n");
+
+out:
+ up(&z_sem);
+
+ return ret;
+}
+
+static int ffsUmountVol(struct super_block *sb)
+{
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ int err = FFS_SUCCESS;
+
+ pr_info("[EXFAT] trying to unmount...\n");
+
+ down(&z_sem);
+
+ /* acquire the lock for file system critical section */
+ down(&p_fs->v_sem);
+
+ fs_sync(sb, false);
+ 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) {
+ pr_info("[EXFAT] unmounted with media errors. Device is already ejected.\n");
+ err = FFS_MEDIAERR;
+ }
+
+ buf_shutdown(sb);
+
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+ up(&z_sem);
+
+ pr_info("[EXFAT] unmounted successfully\n");
+
+ return err;
+}
+
+static int ffsGetVolInfo(struct super_block *sb, struct vol_info_t *info)
+{
+ int err = FFS_SUCCESS;
+ struct 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 */
+ down(&p_fs->v_sem);
+
+ if (p_fs->used_clusters == (u32) ~0)
+ p_fs->used_clusters = p_fs->fs_func->count_used_clusters(sb);
+
+ info->FatType = p_fs->vol_type;
+ info->ClusterSize = p_fs->cluster_size;
+ info->NumClusters = p_fs->num_clusters - 2; /* clu 0 & 1 */
+ info->UsedClusters = p_fs->used_clusters;
+ info->FreeClusters = info->NumClusters - info->UsedClusters;
+
+ if (p_fs->dev_ejected)
+ err = FFS_MEDIAERR;
+
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return err;
+}
+
+static int ffsSyncVol(struct super_block *sb, bool do_sync)
+{
+ int err = FFS_SUCCESS;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ /* acquire the lock for file system critical section */
+ down(&p_fs->v_sem);
+
+ /* synchronize the file system */
+ fs_sync(sb, do_sync);
+ fs_set_vol_flags(sb, VOL_CLEAN);
+
+ if (p_fs->dev_ejected)
+ err = FFS_MEDIAERR;
+
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return err;
+}
+
+/*----------------------------------------------------------------------*/
+/* File Operation Functions */
+/*----------------------------------------------------------------------*/
+
+static int ffsLookupFile(struct inode *inode, char *path, struct file_id_t *fid)
+{
+ int ret, dentry, num_entries;
+ struct chain_t dir;
+ struct uni_name_t uni_name;
+ struct dos_name_t dos_name;
+ struct dentry_t *ep, *ep2;
+ struct entry_set_cache_t *es = NULL;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ pr_debug("%s entered\n", __func__);
+
+ /* check the validity of pointer parameters */
+ if ((fid == NULL) || (path == NULL) || (*path == '\0'))
+ return FFS_ERROR;
+
+ /* acquire the lock for file system critical section */
+ down(&p_fs->v_sem);
+
+ /* check the validity of directory name in the given pathname */
+ ret = resolve_path(inode, path, &dir, &uni_name);
+ if (ret)
+ goto out;
+
+ ret = get_num_entries_and_dos_name(sb, &dir, &uni_name, &num_entries,
+ &dos_name);
+ if (ret)
+ goto out;
+
+ /* 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;
+ goto out;
+ }
+
+ 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) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ ep2 = ep+1;
+ } else {
+ ep = get_entry_in_dir(sb, &dir, dentry, NULL);
+ if (!ep) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ 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)
+ ret = FFS_MEDIAERR;
+out:
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return ret;
+}
+
+static int ffsCreateFile(struct inode *inode, char *path, u8 mode,
+ struct file_id_t *fid)
+{
+ struct chain_t dir;
+ struct uni_name_t uni_name;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ int ret;
+
+ /* check the validity of pointer parameters */
+ if ((fid == NULL) || (path == NULL) || (*path == '\0'))
+ return FFS_ERROR;
+
+ /* acquire the lock for file system critical section */
+ down(&p_fs->v_sem);
+
+ /* check the validity of directory name in the given pathname */
+ ret = resolve_path(inode, path, &dir, &uni_name);
+ if (ret)
+ goto out;
+
+ fs_set_vol_flags(sb, VOL_DIRTY);
+
+ /* create a new file */
+ ret = create_file(inode, &dir, &uni_name, mode, fid);
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+ fs_sync(sb, false);
+ fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+
+out:
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return ret;
+}
+
+static int ffsReadFile(struct inode *inode, struct file_id_t *fid, void *buffer,
+ u64 count, u64 *rcount)
+{
+ s32 offset, sec_offset, clu_offset;
+ u32 clu;
+ int ret;
+ sector_t LogSector;
+ u64 oneblkread, read_bytes;
+ struct buffer_head *tmp_bh = NULL;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_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 */
+ down(&p_fs->v_sem);
+
+ /* check if the given file ID is opened */
+ if (fid->type != TYPE_FILE) {
+ ret = FFS_PERMISSIONERR;
+ goto out;
+ }
+
+ 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;
+ ret = FFS_EOF;
+ goto out;
+ }
+
+ read_bytes = 0;
+
+ while (count > 0) {
+ clu_offset = (s32)(fid->rwoffset >> p_fs->cluster_size_bits);
+ clu = fid->start_clu;
+
+ if (fid->flags == 0x03) {
+ clu += clu_offset;
+ } else {
+ /* hint information */
+ if ((clu_offset > 0) && (fid->hint_last_off > 0) &&
+ (clu_offset >= fid->hint_last_off)) {
+ clu_offset -= fid->hint_last_off;
+ clu = fid->hint_last_clu;
+ }
+
+ while (clu_offset > 0) {
+ /* clu = FAT_read(sb, clu); */
+ if (FAT_read(sb, clu, &clu) == -1)
+ return FFS_MEDIAERR;
+
+ clu_offset--;
+ }
+ }
+
+ /* hint information */
+ fid->hint_last_off = (s32)(fid->rwoffset >>
+ p_fs->cluster_size_bits);
+ fid->hint_last_clu = clu;
+
+ /* byte offset in cluster */
+ offset = (s32)(fid->rwoffset & (p_fs->cluster_size-1));
+
+ /* sector offset in cluster */
+ sec_offset = offset >> p_bd->sector_size_bits;
+
+ /* byte offset in sector */
+ offset &= p_bd->sector_size_mask;
+
+ LogSector = START_SECTOR(clu) + sec_offset;
+
+ oneblkread = (u64)(p_bd->sector_size - offset);
+ if (oneblkread > count)
+ oneblkread = count;
+
+ if ((offset == 0) && (oneblkread == p_bd->sector_size)) {
+ if (sector_read(sb, LogSector, &tmp_bh, 1) !=
+ FFS_SUCCESS)
+ goto err_out;
+ memcpy((char *)buffer + read_bytes,
+ (char *)tmp_bh->b_data, (s32)oneblkread);
+ } else {
+ if (sector_read(sb, LogSector, &tmp_bh, 1) !=
+ FFS_SUCCESS)
+ goto err_out;
+ memcpy((char *)buffer + read_bytes,
+ (char *)tmp_bh->b_data + offset,
+ (s32)oneblkread);
+ }
+ count -= oneblkread;
+ read_bytes += oneblkread;
+ fid->rwoffset += oneblkread;
+ }
+ brelse(tmp_bh);
+
+/* How did this ever work and not leak a brlse()?? */
+err_out:
+ /* set the size of read bytes */
+ if (rcount != NULL)
+ *rcount = read_bytes;
+
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+
+out:
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return ret;
+}
+
+static int ffsWriteFile(struct inode *inode, struct file_id_t *fid,
+ void *buffer, u64 count, u64 *wcount)
+{
+ s32 modified = FALSE, offset, sec_offset, clu_offset;
+ s32 num_clusters, num_alloc, num_alloced = (s32) ~0;
+ int ret = 0;
+ u32 clu, last_clu;
+ sector_t LogSector, sector = 0;
+ u64 oneblkwrite, write_bytes;
+ struct chain_t new_clu;
+ struct timestamp_t tm;
+ struct dentry_t *ep, *ep2;
+ struct entry_set_cache_t *es = NULL;
+ struct buffer_head *tmp_bh = NULL;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_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 */
+ down(&p_fs->v_sem);
+
+ /* check if the given file ID is opened */
+ if (fid->type != TYPE_FILE) {
+ ret = FFS_PERMISSIONERR;
+ goto out;
+ }
+
+ if (fid->rwoffset > fid->size)
+ fid->rwoffset = fid->size;
+
+ if (count == 0) {
+ if (wcount != NULL)
+ *wcount = 0;
+ ret = FFS_SUCCESS;
+ goto out;
+ }
+
+ fs_set_vol_flags(sb, VOL_DIRTY);
+
+ if (fid->size == 0)
+ num_clusters = 0;
+ else
+ num_clusters = (s32)((fid->size-1) >>
+ p_fs->cluster_size_bits) + 1;
+
+ write_bytes = 0;
+
+ while (count > 0) {
+ clu_offset = (s32)(fid->rwoffset >> p_fs->cluster_size_bits);
+ clu = last_clu = fid->start_clu;
+
+ if (fid->flags == 0x03) {
+ if ((clu_offset > 0) && (clu != CLUSTER_32(~0))) {
+ last_clu += clu_offset - 1;
+
+ if (clu_offset == num_clusters)
+ clu = CLUSTER_32(~0);
+ else
+ clu += clu_offset;
+ }
+ } else {
+ /* hint information */
+ if ((clu_offset > 0) && (fid->hint_last_off > 0) &&
+ (clu_offset >= fid->hint_last_off)) {
+ clu_offset -= fid->hint_last_off;
+ clu = fid->hint_last_clu;
+ }
+
+ while ((clu_offset > 0) && (clu != CLUSTER_32(~0))) {
+ last_clu = clu;
+ /* clu = FAT_read(sb, clu); */
+ if (FAT_read(sb, clu, &clu) == -1) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ clu_offset--;
+ }
+ }
+
+ if (clu == CLUSTER_32(~0)) {
+ num_alloc = (s32)((count - 1) >>
+ p_fs->cluster_size_bits) + 1;
+ new_clu.dir = (last_clu == CLUSTER_32(~0)) ?
+ CLUSTER_32(~0) : last_clu+1;
+ new_clu.size = 0;
+ new_clu.flags = fid->flags;
+
+ /* (1) allocate a chain of clusters */
+ num_alloced = p_fs->fs_func->alloc_cluster(sb,
+ num_alloc,
+ &new_clu);
+ if (num_alloced == 0)
+ break;
+ else if (num_alloced < 0) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+
+ /* (2) append to the FAT chain */
+ if (last_clu == CLUSTER_32(~0)) {
+ if (new_clu.flags == 0x01)
+ fid->flags = 0x01;
+ fid->start_clu = new_clu.dir;
+ modified = TRUE;
+ } else {
+ if (new_clu.flags != fid->flags) {
+ exfat_chain_cont_cluster(sb,
+ fid->start_clu,
+ num_clusters);
+ fid->flags = 0x01;
+ modified = TRUE;
+ }
+ if (new_clu.flags == 0x01)
+ FAT_write(sb, last_clu, new_clu.dir);
+ }
+
+ num_clusters += num_alloced;
+ clu = new_clu.dir;
+ }
+
+ /* hint information */
+ fid->hint_last_off = (s32)(fid->rwoffset >>
+ p_fs->cluster_size_bits);
+ fid->hint_last_clu = clu;
+
+ /* byte offset in cluster */
+ offset = (s32)(fid->rwoffset & (p_fs->cluster_size - 1));
+
+ /* sector offset in cluster */
+ sec_offset = offset >> p_bd->sector_size_bits;
+
+ /* byte offset in sector */
+ offset &= p_bd->sector_size_mask;
+
+ LogSector = START_SECTOR(clu) + sec_offset;
+
+ oneblkwrite = (u64)(p_bd->sector_size - offset);
+ if (oneblkwrite > count)
+ oneblkwrite = count;
+
+ if ((offset == 0) && (oneblkwrite == p_bd->sector_size)) {
+ if (sector_read(sb, LogSector, &tmp_bh, 0) !=
+ FFS_SUCCESS)
+ goto err_out;
+ memcpy((char *)tmp_bh->b_data,
+ (char *)buffer + write_bytes, (s32)oneblkwrite);
+ if (sector_write(sb, LogSector, tmp_bh, 0) !=
+ FFS_SUCCESS) {
+ brelse(tmp_bh);
+ goto err_out;
+ }
+ } else {
+ if ((offset > 0) ||
+ ((fid->rwoffset+oneblkwrite) < fid->size)) {
+ if (sector_read(sb, LogSector, &tmp_bh, 1) !=
+ FFS_SUCCESS)
+ goto err_out;
+ } else {
+ if (sector_read(sb, LogSector, &tmp_bh, 0) !=
+ FFS_SUCCESS)
+ goto err_out;
+ }
+
+ memcpy((char *)tmp_bh->b_data + offset,
+ (char *)buffer + write_bytes, (s32)oneblkwrite);
+ if (sector_write(sb, LogSector, tmp_bh, 0) !=
+ FFS_SUCCESS) {
+ brelse(tmp_bh);
+ goto err_out;
+ }
+ }
+
+ count -= oneblkwrite;
+ write_bytes += oneblkwrite;
+ fid->rwoffset += oneblkwrite;
+
+ fid->attr |= ATTR_ARCHIVE;
+
+ if (fid->size < fid->rwoffset) {
+ fid->size = fid->rwoffset;
+ modified = TRUE;
+ }
+ }
+
+ brelse(tmp_bh);
+
+ /* (3) update the direcoty entry */
+ if (p_fs->vol_type == EXFAT) {
+ es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry,
+ ES_ALL_ENTRIES, &ep);
+ if (es == NULL)
+ goto err_out;
+ ep2 = ep+1;
+ } else {
+ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or);
+ if (!ep)
+ goto err_out;
+ ep2 = ep;
+ }
+
+ p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY);
+ p_fs->fs_func->set_entry_attr(ep, fid->attr);
+
+ if (p_fs->vol_type != EXFAT)
+ buf_modify(sb, sector);
+
+ if (modified) {
+ if (p_fs->fs_func->get_entry_flag(ep2) != fid->flags)
+ p_fs->fs_func->set_entry_flag(ep2, fid->flags);
+
+ if (p_fs->fs_func->get_entry_size(ep2) != fid->size)
+ p_fs->fs_func->set_entry_size(ep2, fid->size);
+
+ if (p_fs->fs_func->get_entry_clu0(ep2) != fid->start_clu)
+ p_fs->fs_func->set_entry_clu0(ep2, fid->start_clu);
+
+ if (p_fs->vol_type != EXFAT)
+ buf_modify(sb, sector);
+ }
+
+ if (p_fs->vol_type == EXFAT) {
+ update_dir_checksum_with_entry_set(sb, es);
+ release_entry_set(es);
+ }
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+ fs_sync(sb, false);
+ 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)
+ ret = FFS_FULL;
+
+ else if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+
+out:
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return ret;
+}
+
+static int ffsTruncateFile(struct inode *inode, u64 old_size, u64 new_size)
+{
+ s32 num_clusters;
+ u32 last_clu = CLUSTER_32(0);
+ int ret = 0;
+ sector_t sector = 0;
+ struct chain_t clu;
+ struct timestamp_t tm;
+ struct dentry_t *ep, *ep2;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct file_id_t *fid = &(EXFAT_I(inode)->fid);
+ struct entry_set_cache_t *es = NULL;
+
+ pr_debug("%s entered (inode %p size %llu)\n", __func__, inode,
+ new_size);
+
+ /* acquire the lock for file system critical section */
+ down(&p_fs->v_sem);
+
+ /* check if the given file ID is opened */
+ if (fid->type != TYPE_FILE) {
+ ret = FFS_PERMISSIONERR;
+ goto out;
+ }
+
+ if (fid->size != old_size) {
+ pr_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) {
+ ret = FFS_SUCCESS;
+ goto out;
+ }
+
+ fs_set_vol_flags(sb, VOL_DIRTY);
+
+ clu.dir = fid->start_clu;
+ clu.size = (s32)((old_size-1) >> p_fs->cluster_size_bits) + 1;
+ clu.flags = fid->flags;
+
+ if (new_size > 0) {
+ num_clusters = (s32)((new_size-1) >>
+ p_fs->cluster_size_bits) + 1;
+
+ if (clu.flags == 0x03) {
+ clu.dir += num_clusters;
+ } else {
+ while (num_clusters > 0) {
+ last_clu = clu.dir;
+ if (FAT_read(sb, clu.dir, &clu.dir) == -1)
+ return FFS_MEDIAERR;
+ num_clusters--;
+ }
+ }
+
+ clu.size -= num_clusters;
+ }
+
+ fid->size = new_size;
+ fid->attr |= ATTR_ARCHIVE;
+ if (new_size == 0) {
+ fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01;
+ fid->start_clu = CLUSTER_32(~0);
+ }
+
+ /* (1) update the directory entry */
+ if (p_fs->vol_type == EXFAT) {
+ es = get_entry_set_in_dir(sb, &fid->dir, fid->entry,
+ ES_ALL_ENTRIES, &ep);
+ if (es == NULL) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ ep2 = ep+1;
+ } else {
+ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or);
+ if (!ep) {
+ ret = FFS_MEDIAERR;
+ goto 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);
+
+ p_fs->fs_func->set_entry_size(ep2, new_size);
+ if (new_size == 0) {
+ p_fs->fs_func->set_entry_flag(ep2, 0x01);
+ p_fs->fs_func->set_entry_clu0(ep2, CLUSTER_32(0));
+ }
+
+ if (p_fs->vol_type != EXFAT)
+ buf_modify(sb, sector);
+ else {
+ update_dir_checksum_with_entry_set(sb, es);
+ release_entry_set(es);
+ }
+
+ /* (2) cut off from the FAT chain */
+ if (last_clu != CLUSTER_32(0)) {
+ if (fid->flags == 0x01)
+ FAT_write(sb, last_clu, CLUSTER_32(~0));
+ }
+
+ /* (3) free the clusters */
+ p_fs->fs_func->free_cluster(sb, &clu, 0);
+
+ /* hint information */
+ fid->hint_last_off = -1;
+ if (fid->rwoffset > fid->size)
+ fid->rwoffset = fid->size;
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+ fs_sync(sb, false);
+ fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+
+out:
+ pr_debug("%s exited (%d)\n", __func__, ret);
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return ret;
+}
+
+static void update_parent_info(struct file_id_t *fid,
+ struct inode *parent_inode)
+{
+ struct fs_info_t *p_fs = &(EXFAT_SB(parent_inode->i_sb)->fs_info);
+ struct 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);
+ }
+}
+
+static int ffsMoveFile(struct inode *old_parent_inode, struct file_id_t *fid,
+ struct inode *new_parent_inode, struct dentry *new_dentry)
+{
+ s32 ret;
+ s32 dentry;
+ struct chain_t olddir, newdir;
+ struct chain_t *p_dir = NULL;
+ struct uni_name_t uni_name;
+ struct dentry_t *ep;
+ struct super_block *sb = old_parent_inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ u8 *new_path = (u8 *) new_dentry->d_name.name;
+ struct inode *new_inode = new_dentry->d_inode;
+ int num_entries;
+ struct file_id_t *new_fid = NULL;
+ s32 new_entry = 0;
+
+ /* check the validity of the given file id */
+ if (fid == NULL)
+ return FFS_INVALIDFID;
+
+ /* check the validity of pointer parameters */
+ if ((new_path == NULL) || (*new_path == '\0'))
+ return FFS_ERROR;
+
+ /* acquire the lock for file system critical section */
+ down(&p_fs->v_sem);
+
+ 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)) {
+ ret = FFS_PERMISSIONERR;
+ goto out2;
+ }
+ }
+
+ ep = get_entry_in_dir(sb, &olddir, dentry, NULL);
+ if (!ep) {
+ ret = FFS_MEDIAERR;
+ goto out2;
+ }
+
+ if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY) {
+ ret = FFS_PERMISSIONERR;
+ goto out2;
+ }
+
+ /* check whether new dir is existing directory and empty */
+ if (new_inode) {
+ u32 entry_type;
+
+ ret = FFS_MEDIAERR;
+ new_fid = &EXFAT_I(new_inode)->fid;
+
+ update_parent_info(new_fid, new_parent_inode);
+
+ p_dir = &(new_fid->dir);
+ new_entry = new_fid->entry;
+ ep = get_entry_in_dir(sb, p_dir, new_entry, NULL);
+ if (!ep)
+ goto out;
+
+ entry_type = p_fs->fs_func->get_entry_type(ep);
+
+ if (entry_type == TYPE_DIR) {
+ struct chain_t new_clu;
+
+ new_clu.dir = new_fid->start_clu;
+ new_clu.size = (s32)((new_fid->size - 1) >>
+ p_fs->cluster_size_bits) + 1;
+ new_clu.flags = new_fid->flags;
+
+ if (!is_dir_empty(sb, &new_clu)) {
+ ret = FFS_FILEEXIST;
+ goto out;
+ }
+ }
+ }
+
+ /* check the validity of directory name in the given new pathname */
+ ret = resolve_path(new_parent_inode, new_path, &newdir, &uni_name);
+ if (ret)
+ goto out2;
+
+ fs_set_vol_flags(sb, VOL_DIRTY);
+
+ if (olddir.dir == newdir.dir)
+ ret = rename_file(new_parent_inode, &olddir, dentry, &uni_name,
+ fid);
+ else
+ ret = move_file(new_parent_inode, &olddir, dentry, &newdir,
+ &uni_name, fid);
+
+ if ((ret == FFS_SUCCESS) && new_inode) {
+ /* delete entries of new_dir */
+ ep = get_entry_in_dir(sb, p_dir, new_entry, NULL);
+ if (!ep)
+ goto out;
+
+ num_entries = p_fs->fs_func->count_ext_entries(sb, p_dir,
+ new_entry, ep);
+ if (num_entries < 0)
+ goto out;
+ p_fs->fs_func->delete_dir_entry(sb, p_dir, new_entry, 0,
+ num_entries+1);
+ }
+out:
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+ fs_sync(sb, false);
+ fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+out2:
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return ret;
+}
+
+static int ffsRemoveFile(struct inode *inode, struct file_id_t *fid)
+{
+ s32 dentry;
+ int ret = FFS_SUCCESS;
+ struct chain_t dir, clu_to_free;
+ struct dentry_t *ep;
+ struct super_block *sb = inode->i_sb;
+ struct 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 */
+ down(&p_fs->v_sem);
+
+ 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) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+
+ if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY) {
+ ret = FFS_PERMISSIONERR;
+ goto out;
+ }
+ fs_set_vol_flags(sb, VOL_DIRTY);
+
+ /* (1) update the directory entry */
+ remove_file(inode, &dir, dentry);
+
+ clu_to_free.dir = fid->start_clu;
+ clu_to_free.size = (s32)((fid->size-1) >> p_fs->cluster_size_bits) + 1;
+ clu_to_free.flags = fid->flags;
+
+ /* (2) free the clusters */
+ p_fs->fs_func->free_cluster(sb, &clu_to_free, 0);
+
+ fid->size = 0;
+ fid->start_clu = CLUSTER_32(~0);
+ fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01;
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+ fs_sync(sb, false);
+ fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+out:
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return ret;
+}
+
+#if 0
+/* Not currently wired up */
+static int ffsSetAttr(struct inode *inode, u32 attr)
+{
+ u32 type;
+ int ret = FFS_SUCCESS;
+ sector_t sector = 0;
+ struct dentry_t *ep;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct file_id_t *fid = &(EXFAT_I(inode)->fid);
+ u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0;
+ struct 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;
+ }
+ }
+
+ /* acquire the lock for file system critical section */
+ down(&p_fs->v_sem);
+
+ /* 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) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ } else {
+ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or);
+ if (!ep) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ }
+
+ type = p_fs->fs_func->get_entry_type(ep);
+
+ if (((type == TYPE_FILE) && (attr & ATTR_SUBDIR)) ||
+ ((type == TYPE_DIR) && (!(attr & ATTR_SUBDIR)))) {
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+ else
+ ret = FFS_ERROR;
+
+ if (p_fs->vol_type == EXFAT)
+ release_entry_set(es);
+ goto out;
+ }
+
+ fs_set_vol_flags(sb, VOL_DIRTY);
+
+ /* set the file attribute */
+ fid->attr = attr;
+ p_fs->fs_func->set_entry_attr(ep, attr);
+
+ if (p_fs->vol_type != EXFAT)
+ buf_modify(sb, sector);
+ else {
+ update_dir_checksum_with_entry_set(sb, es);
+ release_entry_set(es);
+ }
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+ fs_sync(sb, false);
+ fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+out:
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return ret;
+}
+#endif
+
+static int ffsReadStat(struct inode *inode, struct dir_entry_t *info)
+{
+ sector_t sector = 0;
+ s32 count;
+ int ret = FFS_SUCCESS;
+ struct chain_t dir;
+ struct uni_name_t uni_name;
+ struct timestamp_t tm;
+ struct dentry_t *ep, *ep2;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct file_id_t *fid = &(EXFAT_I(inode)->fid);
+ struct entry_set_cache_t *es = NULL;
+ u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0;
+
+ pr_debug("%s entered\n", __func__);
+
+ /* acquire the lock for file system critical section */
+ down(&p_fs->v_sem);
+
+ if (is_dir) {
+ if ((fid->dir.dir == p_fs->root_dir) &&
+ (fid->entry == -1)) {
+ info->Attr = ATTR_SUBDIR;
+ memset((char *)&info->CreateTimestamp, 0,
+ sizeof(struct date_time_t));
+ memset((char *)&info->ModifyTimestamp, 0,
+ sizeof(struct date_time_t));
+ memset((char *)&info->AccessTimestamp, 0,
+ sizeof(struct 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) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ info->NumSubdirs = count;
+
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ }
+
+ /* 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) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ ep2 = ep+1;
+ } else {
+ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or);
+ if (!ep) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ ep2 = ep;
+ buf_lock(sb, sector);
+ }
+
+ /* set FILE_INFO structure using the acquired struct dentry_t */
+ info->Attr = p_fs->fs_func->get_entry_attr(ep);
+
+ p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE);
+ info->CreateTimestamp.Year = tm.year;
+ info->CreateTimestamp.Month = tm.mon;
+ info->CreateTimestamp.Day = tm.day;
+ info->CreateTimestamp.Hour = tm.hour;
+ info->CreateTimestamp.Minute = tm.min;
+ info->CreateTimestamp.Second = tm.sec;
+ info->CreateTimestamp.MilliSecond = 0;
+
+ p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY);
+ info->ModifyTimestamp.Year = tm.year;
+ info->ModifyTimestamp.Month = tm.mon;
+ info->ModifyTimestamp.Day = tm.day;
+ info->ModifyTimestamp.Hour = tm.hour;
+ info->ModifyTimestamp.Minute = tm.min;
+ info->ModifyTimestamp.Second = tm.sec;
+ info->ModifyTimestamp.MilliSecond = 0;
+
+ memset((char *) &info->AccessTimestamp, 0, sizeof(struct date_time_t));
+
+ *(uni_name.name) = 0x0;
+ /* XXX this is very bad for exfat cuz name is already included in es.
+ * API should be revised
+ */
+ p_fs->fs_func->get_uni_name_from_ext_entry(sb, &(fid->dir), fid->entry,
+ uni_name.name);
+ if (*uni_name.name == 0x0 && p_fs->vol_type != EXFAT)
+ get_uni_name_from_dos_entry(sb, (struct 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, (struct dos_dentry_t *)ep,
+ &uni_name, 0x0);
+ nls_uniname_to_cstring(sb, info->ShortName, &uni_name);
+ info->NumSubdirs = 0;
+ }
+
+ info->Size = p_fs->fs_func->get_entry_size(ep2);
+
+ if (p_fs->vol_type == EXFAT)
+ release_entry_set(es);
+
+ if (is_dir) {
+ dir.dir = fid->start_clu;
+ dir.flags = 0x01;
+
+ if (info->Size == 0)
+ info->Size = (u64)count_num_clusters(sb, &dir) <<
+ p_fs->cluster_size_bits;
+
+ count = count_dos_name_entries(sb, &dir, TYPE_DIR);
+ if (count < 0) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ info->NumSubdirs += count;
+ }
+
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+
+out:
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ pr_debug("%s exited successfully\n", __func__);
+ return ret;
+}
+
+static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info)
+{
+ sector_t sector = 0;
+ int ret = FFS_SUCCESS;
+ struct timestamp_t tm;
+ struct dentry_t *ep, *ep2;
+ struct entry_set_cache_t *es = NULL;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct file_id_t *fid = &(EXFAT_I(inode)->fid);
+ u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0;
+
+ pr_debug("%s entered (inode %p info %p\n", __func__, inode, info);
+
+ /* acquire the lock for file system critical section */
+ down(&p_fs->v_sem);
+
+ if (is_dir) {
+ if ((fid->dir.dir == p_fs->root_dir) &&
+ (fid->entry == -1)) {
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+ ret = FFS_SUCCESS;
+ goto out;
+ }
+ }
+
+ 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) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ ep2 = ep+1;
+ } else {
+ /* for other than exfat */
+ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or);
+ if (!ep) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ ep2 = ep;
+ }
+
+ p_fs->fs_func->set_entry_attr(ep, info->Attr);
+
+ /* set FILE_INFO structure using the acquired struct 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)
+ ret = FFS_MEDIAERR;
+
+out:
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ pr_debug("%s exited (%d)\n", __func__, ret);
+
+ return ret;
+}
+
+static int ffsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu)
+{
+ s32 num_clusters, num_alloced, modified = FALSE;
+ u32 last_clu;
+ int ret = FFS_SUCCESS;
+ sector_t sector = 0;
+ struct chain_t new_clu;
+ struct dentry_t *ep;
+ struct entry_set_cache_t *es = NULL;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct file_id_t *fid = &(EXFAT_I(inode)->fid);
+
+ /* check the validity of pointer parameters */
+ if (clu == NULL)
+ return FFS_ERROR;
+
+ /* acquire the lock for file system critical section */
+ down(&p_fs->v_sem);
+
+ fid->rwoffset = (s64)(clu_offset) << p_fs->cluster_size_bits;
+
+ if (EXFAT_I(inode)->mmu_private == 0)
+ num_clusters = 0;
+ else
+ num_clusters = (s32)((EXFAT_I(inode)->mmu_private - 1) >>
+ p_fs->cluster_size_bits) + 1;
+
+ *clu = last_clu = fid->start_clu;
+
+ if (fid->flags == 0x03) {
+ if ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) {
+ last_clu += clu_offset - 1;
+
+ if (clu_offset == num_clusters)
+ *clu = CLUSTER_32(~0);
+ else
+ *clu += clu_offset;
+ }
+ } else {
+ /* hint information */
+ if ((clu_offset > 0) && (fid->hint_last_off > 0) &&
+ (clu_offset >= fid->hint_last_off)) {
+ clu_offset -= fid->hint_last_off;
+ *clu = fid->hint_last_clu;
+ }
+
+ while ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) {
+ last_clu = *clu;
+ if (FAT_read(sb, *clu, clu) == -1) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ clu_offset--;
+ }
+ }
+
+ if (*clu == CLUSTER_32(~0)) {
+ fs_set_vol_flags(sb, VOL_DIRTY);
+
+ new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) :
+ last_clu + 1;
+ new_clu.size = 0;
+ new_clu.flags = fid->flags;
+
+ /* (1) allocate a cluster */
+ num_alloced = p_fs->fs_func->alloc_cluster(sb, 1, &new_clu);
+ if (num_alloced < 0) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ } else if (num_alloced == 0) {
+ ret = FFS_FULL;
+ goto out;
+ }
+
+ /* (2) append to the FAT chain */
+ if (last_clu == CLUSTER_32(~0)) {
+ if (new_clu.flags == 0x01)
+ fid->flags = 0x01;
+ fid->start_clu = new_clu.dir;
+ modified = TRUE;
+ } else {
+ if (new_clu.flags != fid->flags) {
+ exfat_chain_cont_cluster(sb, fid->start_clu,
+ num_clusters);
+ fid->flags = 0x01;
+ modified = TRUE;
+ }
+ if (new_clu.flags == 0x01)
+ FAT_write(sb, last_clu, new_clu.dir);
+ }
+
+ num_clusters += num_alloced;
+ *clu = new_clu.dir;
+
+ if (p_fs->vol_type == EXFAT) {
+ es = get_entry_set_in_dir(sb, &fid->dir, fid->entry,
+ ES_ALL_ENTRIES, &ep);
+ if (es == NULL) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ /* 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) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ }
+
+ if (p_fs->fs_func->get_entry_flag(ep) != fid->flags)
+ p_fs->fs_func->set_entry_flag(ep, fid->flags);
+
+ if (p_fs->fs_func->get_entry_clu0(ep) != fid->start_clu)
+ p_fs->fs_func->set_entry_clu0(ep,
+ fid->start_clu);
+
+ if (p_fs->vol_type != EXFAT)
+ buf_modify(sb, sector);
+ }
+
+ if (p_fs->vol_type == EXFAT) {
+ update_dir_checksum_with_entry_set(sb, es);
+ release_entry_set(es);
+ }
+
+ /* add number of new blocks to inode */
+ inode->i_blocks += num_alloced << (p_fs->cluster_size_bits - 9);
+ }
+
+ /* hint information */
+ fid->hint_last_off = (s32)(fid->rwoffset >> p_fs->cluster_size_bits);
+ fid->hint_last_clu = *clu;
+
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+
+out:
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------*/
+/* Directory Operation Functions */
+/*----------------------------------------------------------------------*/
+
+static int ffsCreateDir(struct inode *inode, char *path, struct file_id_t *fid)
+{
+ int ret = FFS_SUCCESS;
+ struct chain_t dir;
+ struct uni_name_t uni_name;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ pr_debug("%s entered\n", __func__);
+
+ /* check the validity of pointer parameters */
+ if ((fid == NULL) || (path == NULL) || (*path == '\0'))
+ return FFS_ERROR;
+
+ /* acquire the lock for file system critical section */
+ down(&p_fs->v_sem);
+
+ /* check the validity of directory name in the given old pathname */
+ ret = resolve_path(inode, path, &dir, &uni_name);
+ if (ret)
+ goto out;
+
+ fs_set_vol_flags(sb, VOL_DIRTY);
+
+ ret = create_dir(inode, &dir, &uni_name, fid);
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+ fs_sync(sb, false);
+ fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+out:
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return ret;
+}
+
+static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry)
+{
+ int i, dentry, clu_offset;
+ int ret = FFS_SUCCESS;
+ s32 dentries_per_clu, dentries_per_clu_bits = 0;
+ u32 type;
+ sector_t sector;
+ struct chain_t dir, clu;
+ struct uni_name_t uni_name;
+ struct timestamp_t tm;
+ struct dentry_t *ep;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct fs_func *fs_func = p_fs->fs_func;
+ struct file_id_t *fid = &(EXFAT_I(inode)->fid);
+
+ /* check the validity of pointer parameters */
+ if (dir_entry == NULL)
+ return FFS_ERROR;
+
+ /* check if the given file ID is opened */
+ if (fid->type != TYPE_DIR)
+ return FFS_PERMISSIONERR;
+
+ /* acquire the lock for file system critical section */
+ down(&p_fs->v_sem);
+
+ if (fid->entry == -1) {
+ dir.dir = p_fs->root_dir;
+ dir.flags = 0x01;
+ } else {
+ dir.dir = fid->start_clu;
+ dir.size = (s32)(fid->size >> p_fs->cluster_size_bits);
+ dir.flags = fid->flags;
+ }
+
+ dentry = (s32)fid->rwoffset;
+
+ if (dir.dir == CLUSTER_32(0)) {
+ /* FAT16 root_dir */
+ dentries_per_clu = p_fs->dentries_in_root;
+
+ if (dentry == dentries_per_clu) {
+ clu.dir = CLUSTER_32(~0);
+ } else {
+ clu.dir = dir.dir;
+ clu.size = dir.size;
+ clu.flags = dir.flags;
+ }
+ } else {
+ dentries_per_clu = p_fs->dentries_per_clu;
+ dentries_per_clu_bits = ilog2(dentries_per_clu);
+
+ clu_offset = dentry >> dentries_per_clu_bits;
+ clu.dir = dir.dir;
+ clu.size = dir.size;
+ clu.flags = dir.flags;
+
+ if (clu.flags == 0x03) {
+ clu.dir += clu_offset;
+ clu.size -= clu_offset;
+ } else {
+ /* hint_information */
+ if ((clu_offset > 0) && (fid->hint_last_off > 0) &&
+ (clu_offset >= fid->hint_last_off)) {
+ clu_offset -= fid->hint_last_off;
+ clu.dir = fid->hint_last_clu;
+ }
+
+ while (clu_offset > 0) {
+ /* clu.dir = FAT_read(sb, clu.dir); */
+ if (FAT_read(sb, clu.dir, &clu.dir) == -1) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ 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) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ type = 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 = fs_func->get_entry_attr(ep);
+
+ 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;
+
+ fs_func->get_entry_time(ep, &tm, TM_MODIFY);
+ dir_entry->ModifyTimestamp.Year = tm.year;
+ dir_entry->ModifyTimestamp.Month = tm.mon;
+ dir_entry->ModifyTimestamp.Day = tm.day;
+ dir_entry->ModifyTimestamp.Hour = tm.hour;
+ dir_entry->ModifyTimestamp.Minute = tm.min;
+ dir_entry->ModifyTimestamp.Second = tm.sec;
+ dir_entry->ModifyTimestamp.MilliSecond = 0;
+
+ memset((char *)&dir_entry->AccessTimestamp, 0,
+ sizeof(struct date_time_t));
+
+ *(uni_name.name) = 0x0;
+ fs_func->get_uni_name_from_ext_entry(sb, &dir, dentry,
+ uni_name.name);
+ if (*uni_name.name == 0x0 && p_fs->vol_type != EXFAT)
+ get_uni_name_from_dos_entry(sb,
+ (struct 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) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ } else {
+ get_uni_name_from_dos_entry(sb,
+ (struct dos_dentry_t *)ep,
+ &uni_name, 0x0);
+ nls_uniname_to_cstring(sb, dir_entry->ShortName,
+ &uni_name);
+ }
+
+ dir_entry->Size = fs_func->get_entry_size(ep);
+
+ /* hint information */
+ if (dir.dir == CLUSTER_32(0)) { /* FAT16 root_dir */
+ } else {
+ fid->hint_last_off = dentry >>
+ dentries_per_clu_bits;
+ fid->hint_last_clu = clu.dir;
+ }
+
+ fid->rwoffset = (s64) ++dentry;
+
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+
+ 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) {
+ ret = FFS_MEDIAERR;
+ goto out;
+ }
+ }
+ }
+
+ *(dir_entry->Name) = '\0';
+
+ fid->rwoffset = (s64) ++dentry;
+
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+
+out:
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return ret;
+}
+
+static int ffsRemoveDir(struct inode *inode, struct file_id_t *fid)
+{
+ s32 dentry;
+ int ret = FFS_SUCCESS;
+ struct chain_t dir, clu_to_free;
+ struct super_block *sb = inode->i_sb;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ /* check the validity of the given file id */
+ if (fid == NULL)
+ return FFS_INVALIDFID;
+
+ 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;
+ }
+
+ /* acquire the lock for file system critical section */
+ down(&p_fs->v_sem);
+
+ clu_to_free.dir = fid->start_clu;
+ clu_to_free.size = (s32)((fid->size-1) >> p_fs->cluster_size_bits) + 1;
+ clu_to_free.flags = fid->flags;
+
+ if (!is_dir_empty(sb, &clu_to_free)) {
+ ret = FFS_FILEEXIST;
+ goto out;
+ }
+
+ fs_set_vol_flags(sb, VOL_DIRTY);
+
+ /* (1) update the directory entry */
+ remove_file(inode, &dir, dentry);
+
+ /* (2) free the clusters */
+ p_fs->fs_func->free_cluster(sb, &clu_to_free, 1);
+
+ fid->size = 0;
+ fid->start_clu = CLUSTER_32(~0);
+ fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01;
+
+#ifdef CONFIG_EXFAT_DELAYED_SYNC
+ fs_sync(sb, false);
+ fs_set_vol_flags(sb, VOL_CLEAN);
+#endif
+
+ if (p_fs->dev_ejected)
+ ret = FFS_MEDIAERR;
+
+out:
+ /* release the lock for file system critical section */
+ up(&p_fs->v_sem);
+
+ return ret;
+}
+
+/*======================================================================*/
+/* Directory Entry Operations */
+/*======================================================================*/
+
+static int exfat_readdir(struct file *filp, struct dir_context *ctx)
+{
+ struct inode *inode = file_inode(filp);
+ struct super_block *sb = inode->i_sb;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ struct fs_info_t *p_fs = &(sbi->fs_info);
+ struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
+ struct dir_entry_t de;
+ unsigned long inum;
+ loff_t cpos;
+ int err = 0;
+
+ __lock_super(sb);
+
+ cpos = ctx->pos;
+ /* Fake . and .. for the root directory. */
+ if ((p_fs->vol_type == EXFAT) || (inode->i_ino == EXFAT_ROOT_INO)) {
+ while (cpos < 2) {
+ if (inode->i_ino == EXFAT_ROOT_INO)
+ inum = EXFAT_ROOT_INO;
+ else if (cpos == 0)
+ inum = inode->i_ino;
+ else /* (cpos == 1) */
+ inum = parent_ino(filp->f_path.dentry);
+
+ if (!dir_emit_dots(filp, ctx))
+ goto out;
+ cpos++;
+ ctx->pos++;
+ }
+ if (cpos == 2)
+ cpos = 0;
+ }
+ if (cpos & (DENTRY_SIZE - 1)) {
+ err = -ENOENT;
+ goto out;
+ }
+
+get_new:
+ EXFAT_I(inode)->fid.size = i_size_read(inode);
+ EXFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS;
+
+ err = ffsReadDir(inode, &de);
+ if (err) {
+ /* at least we tried to read a sector
+ * move cpos to next sector position (should be aligned)
+ */
+ if (err == FFS_MEDIAERR) {
+ cpos += 1 << p_bd->sector_size_bits;
+ cpos &= ~((1 << p_bd->sector_size_bits)-1);
+ }
+
+ err = -EIO;
+ goto end_of_dir;
+ }
+
+ cpos = EXFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS;
+
+ if (!de.Name[0])
+ goto end_of_dir;
+
+ if (!memcmp(de.ShortName, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) {
+ inum = inode->i_ino;
+ } else if (!memcmp(de.ShortName, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) {
+ inum = parent_ino(filp->f_path.dentry);
+ } else {
+ loff_t i_pos = ((loff_t) EXFAT_I(inode)->fid.start_clu << 32) |
+ ((EXFAT_I(inode)->fid.rwoffset-1) & 0xffffffff);
+ struct inode *tmp = exfat_iget(sb, i_pos);
+
+ if (tmp) {
+ inum = tmp->i_ino;
+ iput(tmp);
+ } else {
+ inum = iunique(sb, EXFAT_ROOT_INO);
+ }
+ }
+
+ if (!dir_emit(ctx, de.Name, strlen(de.Name), inum,
+ (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG))
+ goto out;
+
+ ctx->pos = cpos;
+ goto get_new;
+
+end_of_dir:
+ ctx->pos = cpos;
+out:
+ __unlock_super(sb);
+ return err;
+}
+
+static int exfat_ioctl_volume_id(struct inode *dir)
+{
+ struct super_block *sb = dir->i_sb;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ struct fs_info_t *p_fs = &(sbi->fs_info);
+
+ return p_fs->vol_id;
+}
+
+static long exfat_generic_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+struct inode *inode = filp->f_path.dentry->d_inode;
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+ unsigned int flags;
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+
+ switch (cmd) {
+ case EXFAT_IOCTL_GET_VOLUME_ID:
+ return exfat_ioctl_volume_id(inode);
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+ case EXFAT_IOC_GET_DEBUGFLAGS: {
+ struct super_block *sb = inode->i_sb;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+ flags = sbi->debug_flags;
+ return put_user(flags, (int __user *)arg);
+ }
+ case EXFAT_IOC_SET_DEBUGFLAGS: {
+ struct super_block *sb = inode->i_sb;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (get_user(flags, (int __user *) arg))
+ return -EFAULT;
+
+ __lock_super(sb);
+ sbi->debug_flags = flags;
+ __unlock_super(sb);
+
+ return 0;
+ }
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+ default:
+ return -ENOTTY; /* Inappropriate ioctl for device */
+ }
+}
+
+static const struct file_operations exfat_dir_operations = {
+ .llseek = generic_file_llseek,
+ .read = generic_read_dir,
+ .iterate = exfat_readdir,
+ .unlocked_ioctl = exfat_generic_ioctl,
+ .fsync = generic_file_fsync,
+};
+
+static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+ bool excl)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode;
+ struct file_id_t fid;
+ loff_t i_pos;
+ int err;
+
+ __lock_super(sb);
+
+ pr_debug("%s entered\n", __func__);
+
+ err = ffsCreateFile(dir, (u8 *) dentry->d_name.name, FM_REGULAR, &fid);
+ if (err) {
+ if (err == FFS_INVALIDPATH)
+ err = -EINVAL;
+ else if (err == FFS_FILEEXIST)
+ err = -EEXIST;
+ else if (err == FFS_FULL)
+ err = -ENOSPC;
+ else if (err == FFS_NAMETOOLONG)
+ err = -ENAMETOOLONG;
+ else
+ err = -EIO;
+ goto out;
+ }
+ INC_IVERSION(dir);
+ dir->i_ctime = dir->i_mtime = dir->i_atime = current_time(dir);
+ if (IS_DIRSYNC(dir))
+ (void) exfat_sync_inode(dir);
+ else
+ mark_inode_dirty(dir);
+
+ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff);
+
+ inode = exfat_build_inode(sb, &fid, i_pos);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto out;
+ }
+ INC_IVERSION(inode);
+ inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+ /*
+ * timestamp is already written, so mark_inode_dirty() is unnecessary.
+ */
+
+ dentry->d_time = GET_IVERSION(dentry->d_parent->d_inode);
+ d_instantiate(dentry, inode);
+
+out:
+ __unlock_super(sb);
+ pr_debug("%s exited\n", __func__);
+ return err;
+}
+
+static int exfat_find(struct inode *dir, struct qstr *qname,
+ struct file_id_t *fid)
+{
+ int err;
+
+ if (qname->len == 0)
+ return -ENOENT;
+
+ err = ffsLookupFile(dir, (u8 *) qname->name, fid);
+ if (err)
+ return -ENOENT;
+
+ return 0;
+}
+
+static int exfat_d_anon_disconn(struct dentry *dentry)
+{
+ return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED);
+}
+
+static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode;
+ struct dentry *alias;
+ int err;
+ struct file_id_t fid;
+ loff_t i_pos;
+ u64 ret;
+ mode_t i_mode;
+
+ __lock_super(sb);
+ pr_debug("%s entered\n", __func__);
+ err = exfat_find(dir, &dentry->d_name, &fid);
+ if (err) {
+ if (err == -ENOENT) {
+ inode = NULL;
+ goto out;
+ }
+ goto error;
+ }
+
+ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff);
+ inode = exfat_build_inode(sb, &fid, i_pos);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto error;
+ }
+
+ i_mode = inode->i_mode;
+ if (S_ISLNK(i_mode) && !EXFAT_I(inode)->target) {
+ EXFAT_I(inode)->target = kmalloc(i_size_read(inode) + 1,
+ GFP_KERNEL);
+ if (!EXFAT_I(inode)->target) {
+ err = -ENOMEM;
+ goto error;
+ }
+ ffsReadFile(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)) {
+ BUG_ON(d_unhashed(alias));
+ if (!S_ISDIR(i_mode))
+ d_move(alias, dentry);
+ iput(inode);
+ __unlock_super(sb);
+ pr_debug("%s exited 1\n", __func__);
+ return alias;
+ }
+ dput(alias);
+out:
+ __unlock_super(sb);
+ dentry->d_time = GET_IVERSION(dentry->d_parent->d_inode);
+ dentry = d_splice_alias(inode, dentry);
+ if (dentry)
+ dentry->d_time = GET_IVERSION(dentry->d_parent->d_inode);
+ pr_debug("%s exited 2\n", __func__);
+ return dentry;
+
+error:
+ __unlock_super(sb);
+ pr_debug("%s exited 3\n", __func__);
+ return ERR_PTR(err);
+}
+
+static inline unsigned long exfat_hash(loff_t i_pos)
+{
+ return hash_32(i_pos, EXFAT_HASH_BITS);
+}
+
+static void exfat_attach(struct inode *inode, loff_t i_pos)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+ struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos);
+
+ spin_lock(&sbi->inode_hash_lock);
+ EXFAT_I(inode)->i_pos = i_pos;
+ hlist_add_head(&EXFAT_I(inode)->i_hash_fat, head);
+ spin_unlock(&sbi->inode_hash_lock);
+}
+
+static void exfat_detach(struct inode *inode)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+
+ spin_lock(&sbi->inode_hash_lock);
+ hlist_del_init(&EXFAT_I(inode)->i_hash_fat);
+ EXFAT_I(inode)->i_pos = 0;
+ spin_unlock(&sbi->inode_hash_lock);
+}
+
+static int exfat_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct super_block *sb = dir->i_sb;
+ int err;
+
+ __lock_super(sb);
+
+ pr_debug("%s entered\n", __func__);
+
+ EXFAT_I(inode)->fid.size = i_size_read(inode);
+
+ err = ffsRemoveFile(dir, &(EXFAT_I(inode)->fid));
+ if (err) {
+ if (err == FFS_PERMISSIONERR)
+ err = -EPERM;
+ else
+ err = -EIO;
+ goto out;
+ }
+ INC_IVERSION(dir);
+ dir->i_mtime = dir->i_atime = current_time(dir);
+ if (IS_DIRSYNC(dir))
+ (void) exfat_sync_inode(dir);
+ else
+ mark_inode_dirty(dir);
+
+ clear_nlink(inode);
+ inode->i_mtime = inode->i_atime = current_time(inode);
+ exfat_detach(inode);
+ remove_inode_hash(inode);
+
+out:
+ __unlock_super(sb);
+ pr_debug("%s exited\n", __func__);
+ 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 file_id_t fid;
+ loff_t i_pos;
+ int err;
+ u64 len = (u64) strlen(target);
+ u64 ret;
+
+ __lock_super(sb);
+
+ pr_debug("%s entered\n", __func__);
+
+ err = ffsCreateFile(dir, (u8 *) dentry->d_name.name, FM_SYMLINK, &fid);
+ if (err) {
+ if (err == FFS_INVALIDPATH)
+ err = -EINVAL;
+ else if (err == FFS_FILEEXIST)
+ err = -EEXIST;
+ else if (err == FFS_FULL)
+ err = -ENOSPC;
+ else
+ err = -EIO;
+ goto out;
+ }
+
+ err = ffsWriteFile(dir, &fid, (char *) target, len, &ret);
+
+ if (err) {
+ ffsRemoveFile(dir, &fid);
+
+ if (err == FFS_FULL)
+ err = -ENOSPC;
+ else
+ err = -EIO;
+ goto out;
+ }
+
+ INC_IVERSION(dir);
+ dir->i_ctime = dir->i_mtime = dir->i_atime = current_time(dir);
+ if (IS_DIRSYNC(dir))
+ (void) exfat_sync_inode(dir);
+ else
+ mark_inode_dirty(dir);
+
+ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff);
+
+ inode = exfat_build_inode(sb, &fid, i_pos);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto out;
+ }
+ INC_IVERSION(inode);
+ inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+ /* timestamp is already written, so mark_inode_dirty() is unneeded. */
+
+ EXFAT_I(inode)->target = kmalloc(len+1, GFP_KERNEL);
+ if (!EXFAT_I(inode)->target) {
+ err = -ENOMEM;
+ goto out;
+ }
+ memcpy(EXFAT_I(inode)->target, target, len+1);
+
+ dentry->d_time = GET_IVERSION(dentry->d_parent->d_inode);
+ d_instantiate(dentry, inode);
+
+out:
+ __unlock_super(sb);
+ pr_debug("%s exited\n", __func__);
+ return err;
+}
+
+static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode;
+ struct file_id_t fid;
+ loff_t i_pos;
+ int err;
+
+ __lock_super(sb);
+
+ pr_debug("%s entered\n", __func__);
+
+ err = ffsCreateDir(dir, (u8 *) dentry->d_name.name, &fid);
+ if (err) {
+ if (err == FFS_INVALIDPATH)
+ err = -EINVAL;
+ else if (err == FFS_FILEEXIST)
+ err = -EEXIST;
+ else if (err == FFS_FULL)
+ err = -ENOSPC;
+ else if (err == FFS_NAMETOOLONG)
+ err = -ENAMETOOLONG;
+ else
+ err = -EIO;
+ goto out;
+ }
+ INC_IVERSION(dir);
+ dir->i_ctime = dir->i_mtime = dir->i_atime = current_time(dir);
+ if (IS_DIRSYNC(dir))
+ (void) exfat_sync_inode(dir);
+ else
+ mark_inode_dirty(dir);
+ inc_nlink(dir);
+
+ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff);
+
+ inode = exfat_build_inode(sb, &fid, i_pos);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto out;
+ }
+ INC_IVERSION(inode);
+ inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+ /* timestamp is already written, so mark_inode_dirty() is unneeded. */
+
+ dentry->d_time = GET_IVERSION(dentry->d_parent->d_inode);
+ d_instantiate(dentry, inode);
+
+out:
+ __unlock_super(sb);
+ pr_debug("%s exited\n", __func__);
+ return err;
+}
+
+static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct super_block *sb = dir->i_sb;
+ int err;
+
+ __lock_super(sb);
+
+ pr_debug("%s entered\n", __func__);
+
+ EXFAT_I(inode)->fid.size = i_size_read(inode);
+
+ err = ffsRemoveDir(dir, &(EXFAT_I(inode)->fid));
+ if (err) {
+ if (err == FFS_INVALIDPATH)
+ err = -EINVAL;
+ else if (err == FFS_FILEEXIST)
+ err = -ENOTEMPTY;
+ else if (err == FFS_NOTFOUND)
+ err = -ENOENT;
+ else if (err == FFS_DIRBUSY)
+ err = -EBUSY;
+ else
+ err = -EIO;
+ goto out;
+ }
+ INC_IVERSION(dir);
+ dir->i_mtime = dir->i_atime = current_time(dir);
+ if (IS_DIRSYNC(dir))
+ (void) exfat_sync_inode(dir);
+ else
+ mark_inode_dirty(dir);
+ drop_nlink(dir);
+
+ clear_nlink(inode);
+ inode->i_mtime = inode->i_atime = current_time(inode);
+ exfat_detach(inode);
+ remove_inode_hash(inode);
+
+out:
+ __unlock_super(sb);
+ pr_debug("%s exited\n", __func__);
+ return err;
+}
+
+static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ unsigned int flags)
+{
+ struct inode *old_inode, *new_inode;
+ struct super_block *sb = old_dir->i_sb;
+ loff_t i_pos;
+ int err;
+
+ if (flags)
+ return -EINVAL;
+
+ __lock_super(sb);
+
+ pr_debug("%s entered\n", __func__);
+
+ old_inode = old_dentry->d_inode;
+ new_inode = new_dentry->d_inode;
+
+ EXFAT_I(old_inode)->fid.size = i_size_read(old_inode);
+
+ err = ffsMoveFile(old_dir, &(EXFAT_I(old_inode)->fid), new_dir,
+ new_dentry);
+ if (err) {
+ if (err == FFS_PERMISSIONERR)
+ err = -EPERM;
+ else if (err == FFS_INVALIDPATH)
+ err = -EINVAL;
+ else if (err == FFS_FILEEXIST)
+ err = -EEXIST;
+ else if (err == FFS_NOTFOUND)
+ err = -ENOENT;
+ else if (err == FFS_FULL)
+ err = -ENOSPC;
+ else
+ err = -EIO;
+ goto out;
+ }
+ INC_IVERSION(new_dir);
+ new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
+ current_time(new_dir);
+ if (IS_DIRSYNC(new_dir))
+ (void) exfat_sync_inode(new_dir);
+ else
+ mark_inode_dirty(new_dir);
+
+ i_pos = ((loff_t) EXFAT_I(old_inode)->fid.dir.dir << 32) |
+ (EXFAT_I(old_inode)->fid.entry & 0xffffffff);
+
+ exfat_detach(old_inode);
+ exfat_attach(old_inode, i_pos);
+ if (IS_DIRSYNC(new_dir))
+ (void) exfat_sync_inode(old_inode);
+ else
+ mark_inode_dirty(old_inode);
+
+ if ((S_ISDIR(old_inode->i_mode)) && (old_dir != new_dir)) {
+ drop_nlink(old_dir);
+ if (!new_inode)
+ inc_nlink(new_dir);
+ }
+ INC_IVERSION(old_dir);
+ old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir);
+ if (IS_DIRSYNC(old_dir))
+ (void) exfat_sync_inode(old_dir);
+ else
+ mark_inode_dirty(old_dir);
+
+ if (new_inode) {
+ exfat_detach(new_inode);
+ drop_nlink(new_inode);
+ if (S_ISDIR(new_inode->i_mode))
+ drop_nlink(new_inode);
+ new_inode->i_ctime = current_time(new_inode);
+ }
+
+out:
+ __unlock_super(sb);
+ pr_debug("%s exited\n", __func__);
+ return err;
+}
+
+static int exfat_cont_expand(struct inode *inode, loff_t size)
+{
+ struct address_space *mapping = inode->i_mapping;
+ loff_t start = i_size_read(inode), count = size - i_size_read(inode);
+ int err, err2;
+
+ err = generic_cont_expand_simple(inode, size);
+ if (err != 0)
+ return err;
+
+ inode->i_ctime = inode->i_mtime = current_time(inode);
+ mark_inode_dirty(inode);
+
+ if (IS_SYNC(inode)) {
+ err = filemap_fdatawrite_range(mapping, start,
+ start + count - 1);
+ err2 = sync_mapping_buffers(mapping);
+ err = (err) ? (err) : (err2);
+ err2 = write_inode_now(inode, 1);
+ err = (err) ? (err) : (err2);
+ if (!err)
+ 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 (!uid_eq(current_fsuid(), inode->i_uid)) {
+ if (in_group_p(inode->i_gid))
+ allow_utime >>= 3;
+ if (allow_utime & MAY_WRITE)
+ return 1;
+ }
+
+ /* use a default check */
+ return 0;
+}
+
+static int exfat_sanitize_mode(const struct exfat_sb_info *sbi,
+ struct inode *inode, umode_t *mode_ptr)
+{
+ mode_t i_mode, mask, perm;
+
+ i_mode = inode->i_mode;
+
+ if (S_ISREG(i_mode) || S_ISLNK(i_mode))
+ mask = sbi->options.fs_fmask;
+ else
+ mask = sbi->options.fs_dmask;
+
+ perm = *mode_ptr & ~(S_IFMT | mask);
+
+ /* Of the r and x bits, all (subject to umask) must be present.*/
+ if ((perm & 0555) != (i_mode & 0555))
+ 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 & 0222) && ((perm & 0222) != (0222 & ~mask)))
+ return -EPERM;
+ } else {
+ /*
+ * If exfat_mode_can_hold_ro(inode) is false, can't change w
+ * bits.
+ */
+ if ((perm & 0222) != (0222 & ~mask))
+ return -EPERM;
+ }
+
+ *mode_ptr &= S_IFMT | perm;
+
+ return 0;
+}
+
+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);
+ struct 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 = ffsTruncateFile(inode, old_size, i_size_read(inode));
+ if (err)
+ goto out;
+
+ inode->i_ctime = inode->i_mtime = current_time(inode);
+ if (IS_DIRSYNC(inode))
+ (void) exfat_sync_inode(inode);
+ else
+ mark_inode_dirty(inode);
+
+ inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) &
+ ~((loff_t)p_fs->cluster_size - 1)) >> 9;
+out:
+ __unlock_super(sb);
+}
+
+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;
+
+ pr_debug("%s entered\n", __func__);
+
+ 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 = setattr_prepare(dentry, attr);
+ attr->ia_valid = ia_valid;
+ if (error)
+ return error;
+
+ if (((attr->ia_valid & ATTR_UID) &&
+ (!uid_eq(attr->ia_uid, sbi->options.fs_uid))) ||
+ ((attr->ia_valid & ATTR_GID) &&
+ (!gid_eq(attr->ia_gid, sbi->options.fs_gid))) ||
+ ((attr->ia_valid & ATTR_MODE) &&
+ (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | 0777)))) {
+ return -EPERM;
+ }
+
+ /*
+ * We don't return -EPERM here. Yes, strange, but this is too
+ * old behavior.
+ */
+ if (attr->ia_valid & ATTR_MODE) {
+ if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0)
+ attr->ia_valid &= ~ATTR_MODE;
+ }
+
+ EXFAT_I(inode)->fid.size = i_size_read(inode);
+
+ if (attr->ia_valid & ATTR_SIZE) {
+ old_size = i_size_read(inode);
+ down_write(&EXFAT_I(inode)->truncate_lock);
+ truncate_setsize(inode, attr->ia_size);
+ exfat_truncate(inode, old_size);
+ up_write(&EXFAT_I(inode)->truncate_lock);
+ }
+ setattr_copy(inode, attr);
+ mark_inode_dirty(inode);
+
+ pr_debug("%s exited\n", __func__);
+ return error;
+}
+
+static int exfat_getattr(const struct path *path, struct kstat *stat,
+ u32 request_mask, unsigned int flags)
+{
+ struct inode *inode = path->dentry->d_inode;
+
+ pr_debug("%s entered\n", __func__);
+
+ generic_fillattr(inode, stat);
+ stat->blksize = EXFAT_SB(inode->i_sb)->fs_info.cluster_size;
+
+ pr_debug("%s exited\n", __func__);
+ return 0;
+}
+
+static 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 const char *exfat_get_link(struct dentry *dentry, struct inode *inode,
+ struct delayed_call *done)
+{
+ struct exfat_inode_info *ei = EXFAT_I(inode);
+
+ if (ei->target != NULL) {
+ char *cookie = ei->target;
+
+ if (cookie != NULL)
+ return (char *)(ei->target);
+ }
+ return NULL;
+}
+
+static const struct inode_operations exfat_symlink_inode_operations = {
+ .get_link = exfat_get_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);
+ ffsSyncVol(sb, false);
+ return 0;
+}
+
+static const struct file_operations exfat_file_operations = {
+ .llseek = generic_file_llseek,
+ .read_iter = generic_file_read_iter,
+ .write_iter = generic_file_write_iter,
+ .mmap = generic_file_mmap,
+ .release = exfat_file_release,
+ .unlocked_ioctl = exfat_generic_ioctl,
+ .fsync = generic_file_fsync,
+ .splice_read = generic_file_splice_read,
+};
+
+static const struct inode_operations exfat_file_inode_operations = {
+ .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);
+ struct fs_info_t *p_fs = &(sbi->fs_info);
+ struct 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;
+ }
+
+ /* cluster offset */
+ clu_offset = sector >> p_fs->sectors_per_clu_bits;
+
+ /* sector offset in cluster */
+ sec_offset = sector & (p_fs->sectors_per_clu - 1);
+
+ EXFAT_I(inode)->fid.size = i_size_read(inode);
+
+ err = ffsMapCluster(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)
+{
+ return mpage_readpage(page, exfat_get_block);
+}
+
+static int exfat_readpages(struct file *file, struct address_space *mapping,
+ struct list_head *pages, unsigned int nr_pages)
+{
+ return mpage_readpages(mapping, pages, nr_pages, exfat_get_block);
+}
+
+static int exfat_writepage(struct page *page, struct writeback_control *wbc)
+{
+ return block_write_full_page(page, exfat_get_block, wbc);
+}
+
+static int exfat_writepages(struct address_space *mapping,
+ struct writeback_control *wbc)
+{
+ return mpage_writepages(mapping, wbc, exfat_get_block);
+}
+
+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, i_size_read(inode));
+ EXFAT_I(inode)->fid.size = i_size_read(inode);
+ exfat_truncate(inode, i_size_read(inode));
+ }
+}
+
+static int exfat_write_begin(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned int len, unsigned int flags,
+ struct page **pagep, void **fsdata)
+{
+ int ret;
+
+ *pagep = NULL;
+ ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+ exfat_get_block,
+ &EXFAT_I(mapping->host)->mmu_private);
+
+ if (ret < 0)
+ exfat_write_failed(mapping, pos+len);
+ return ret;
+}
+
+static int exfat_write_end(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned int len, unsigned int copied,
+ struct page *pagep, void *fsdata)
+{
+ struct inode *inode = mapping->host;
+ struct file_id_t *fid = &(EXFAT_I(inode)->fid);
+ int err;
+
+ err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata);
+
+ if (err < len)
+ exfat_write_failed(mapping, pos+len);
+
+ if (!(err < 0) && !(fid->attr & ATTR_ARCHIVE)) {
+ inode->i_mtime = inode->i_ctime = current_time(inode);
+ fid->attr |= ATTR_ARCHIVE;
+ mark_inode_dirty(inode);
+ }
+ return err;
+}
+
+static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
+{
+ struct inode *inode = iocb->ki_filp->f_mapping->host;
+ struct address_space *mapping = iocb->ki_filp->f_mapping;
+ ssize_t ret;
+ int rw;
+
+ rw = iov_iter_rw(iter);
+
+ if (rw == WRITE) {
+ if (EXFAT_I(inode)->mmu_private < iov_iter_count(iter))
+ return 0;
+ }
+ ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block);
+
+ if ((ret < 0) && (rw & WRITE))
+ exfat_write_failed(mapping, iov_iter_count(iter));
+ return ret;
+}
+
+static sector_t _exfat_bmap(struct address_space *mapping, sector_t block)
+{
+ sector_t blocknr;
+
+ /* exfat_get_cluster() assumes the requested blocknr isn't truncated. */
+ down_read(&EXFAT_I(mapping->host)->truncate_lock);
+ blocknr = generic_block_bmap(mapping, block, exfat_get_block);
+ up_read(&EXFAT_I(mapping->host)->truncate_lock);
+
+ return blocknr;
+}
+
+static const struct address_space_operations exfat_aops = {
+ .readpage = exfat_readpage,
+ .readpages = exfat_readpages,
+ .writepage = exfat_writepage,
+ .writepages = exfat_writepages,
+ .write_begin = exfat_write_begin,
+ .write_end = exfat_write_end,
+ .direct_IO = exfat_direct_IO,
+ .bmap = _exfat_bmap
+};
+
+/*======================================================================*/
+/* Super Operations */
+/*======================================================================*/
+
+static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ struct exfat_inode_info *info;
+ struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos);
+ struct inode *inode = NULL;
+
+ spin_lock(&sbi->inode_hash_lock);
+ hlist_for_each_entry(info, head, i_hash_fat) {
+ BUG_ON(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;
+}
+
+/* doesn't deal with root inode */
+static int exfat_fill_inode(struct inode *inode, struct file_id_t *fid)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+ struct fs_info_t *p_fs = &(sbi->fs_info);
+ struct dir_entry_t info;
+
+ memcpy(&(EXFAT_I(inode)->fid), fid, sizeof(struct file_id_t));
+
+ ffsReadStat(inode, &info);
+
+ EXFAT_I(inode)->i_pos = 0;
+ EXFAT_I(inode)->target = NULL;
+ inode->i_uid = sbi->options.fs_uid;
+ inode->i_gid = sbi->options.fs_gid;
+ INC_IVERSION(inode);
+ inode->i_generation = get_seconds();
+
+ if (info.Attr & ATTR_SUBDIR) { /* directory */
+ inode->i_generation &= ~1;
+ inode->i_mode = exfat_make_mode(sbi, info.Attr, 0777);
+ inode->i_op = &exfat_dir_inode_operations;
+ inode->i_fop = &exfat_dir_operations;
+
+ i_size_write(inode, info.Size);
+ EXFAT_I(inode)->mmu_private = i_size_read(inode);
+ set_nlink(inode, info.NumSubdirs);
+ } else if (info.Attr & ATTR_SYMLINK) { /* symbolic link */
+ inode->i_generation |= 1;
+ inode->i_mode = exfat_make_mode(sbi, info.Attr, 0777);
+ 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, 0777);
+ 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,
+ struct file_id_t *fid, loff_t i_pos)
+{
+ struct inode *inode;
+ int err;
+
+ inode = exfat_iget(sb, i_pos);
+ if (inode)
+ goto out;
+ inode = new_inode(sb);
+ if (!inode) {
+ inode = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+ inode->i_ino = iunique(sb, EXFAT_ROOT_INO);
+ SET_IVERSION(inode, 1);
+ err = exfat_fill_inode(inode, fid);
+ if (err) {
+ iput(inode);
+ inode = ERR_PTR(err);
+ goto out;
+ }
+ exfat_attach(inode, i_pos);
+ insert_inode_hash(inode);
+out:
+ return inode;
+}
+
+static int exfat_sync_inode(struct inode *inode)
+{
+ return exfat_write_inode(inode, NULL);
+}
+
+static struct inode *exfat_alloc_inode(struct super_block *sb)
+{
+ struct exfat_inode_info *ei;
+
+ ei = kmem_cache_alloc(exfat_inode_cachep, GFP_NOFS);
+ if (!ei)
+ return NULL;
+
+ init_rwsem(&ei->truncate_lock);
+
+ return &ei->vfs_inode;
+}
+
+static void exfat_destroy_inode(struct inode *inode)
+{
+ if (EXFAT_I(inode)->target)
+ kfree(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);
+ struct 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);
+
+ ffsWriteStat(inode, &info);
+
+ return 0;
+}
+
+static void exfat_evict_inode(struct inode *inode)
+{
+ truncate_inode_pages(&inode->i_data, 0);
+
+ if (!inode->i_nlink)
+ i_size_write(inode, 0);
+ invalidate_inode_buffers(inode);
+ clear_inode(inode);
+ exfat_detach(inode);
+
+ remove_inode_hash(inode);
+}
+
+static void exfat_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_init is in exfat_fill_super function. only for 3.7+ */
+ 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);
+
+ ffsUmountVol(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_rdonly(sb))
+ ffsSyncVol(sb, true);
+
+ __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 = ffsSyncVol(sb, true);
+ __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);
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+ struct vol_info_t info;
+
+ if (p_fs->used_clusters == (u32) ~0) {
+ if (ffsGetVolInfo(sb, &info) == FFS_MEDIAERR)
+ 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)
+ pr_info("[EXFAT] statfs on device that is ejected\n");
+ }
+
+ buf->f_type = sb->s_magic;
+ buf->f_bsize = info.ClusterSize;
+ buf->f_blocks = info.NumClusters;
+ buf->f_bfree = info.FreeClusters;
+ buf->f_bavail = info.FreeClusters;
+ buf->f_fsid.val[0] = (u32)id;
+ buf->f_fsid.val[1] = (u32)(id >> 32);
+ buf->f_namelen = 260;
+
+ return 0;
+}
+
+static int exfat_remount(struct super_block *sb, int *flags, char *data)
+{
+ *flags |= SB_NODIRATIME;
+ return 0;
+}
+
+static int exfat_show_options(struct seq_file *m, struct dentry *root)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(root->d_sb);
+ struct exfat_mount_options *opts = &sbi->options;
+
+ if (__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));
+ seq_printf(m, ",fmask=%04o", opts->fs_fmask);
+ seq_printf(m, ",dmask=%04o", opts->fs_dmask);
+ if (opts->allow_utime)
+ seq_printf(m, ",allow_utime=%04o", opts->allow_utime);
+ if (sbi->nls_disk)
+ seq_printf(m, ",codepage=%s", sbi->nls_disk->charset);
+ if (sbi->nls_io)
+ seq_printf(m, ",iocharset=%s", sbi->nls_io->charset);
+ seq_printf(m, ",namecase=%u", opts->casesensitive);
+ if (opts->errors == EXFAT_ERRORS_CONT)
+ seq_puts(m, ",errors=continue");
+ else if (opts->errors == EXFAT_ERRORS_PANIC)
+ seq_puts(m, ",errors=panic");
+ else
+ seq_puts(m, ",errors=remount-ro");
+#ifdef CONFIG_EXFAT_DISCARD
+ if (opts->discard)
+ seq_puts(m, ",discard");
+#endif
+ return 0;
+}
+
+static const struct super_operations exfat_sops = {
+ .alloc_inode = exfat_alloc_inode,
+ .destroy_inode = exfat_destroy_inode,
+ .write_inode = exfat_write_inode,
+ .evict_inode = exfat_evict_inode,
+ .put_super = exfat_put_super,
+ .sync_fs = exfat_sync_fs,
+ .statfs = exfat_statfs,
+ .remount_fs = exfat_remount,
+ .show_options = exfat_show_options,
+};
+
+/*======================================================================*/
+/* Export Operations */
+/*======================================================================*/
+
+static struct inode *exfat_nfs_get_inode(struct super_block *sb, u64 ino,
+ u32 generation)
+{
+ struct inode *inode = NULL;
+
+ if (ino < EXFAT_ROOT_INO)
+ return inode;
+ inode = ilookup(sb, ino);
+
+ if (inode && generation && (inode->i_generation != generation)) {
+ iput(inode);
+ inode = NULL;
+ }
+
+ return inode;
+}
+
+static struct dentry *exfat_fh_to_dentry(struct super_block *sb,
+ struct fid *fid, int fh_len,
+ int fh_type)
+{
+ return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
+ exfat_nfs_get_inode);
+}
+
+static struct dentry *exfat_fh_to_parent(struct super_block *sb,
+ struct fid *fid, int fh_len,
+ int fh_type)
+{
+ return generic_fh_to_parent(sb, fid, fh_len, fh_type,
+ exfat_nfs_get_inode);
+}
+
+static const struct export_operations exfat_export_ops = {
+ .fh_to_dentry = exfat_fh_to_dentry,
+ .fh_to_parent = exfat_fh_to_parent,
+};
+
+/*======================================================================*/
+/* Super Block Read Operations */
+/*======================================================================*/
+
+enum {
+ Opt_uid,
+ Opt_gid,
+ Opt_umask,
+ Opt_dmask,
+ Opt_fmask,
+ Opt_allow_utime,
+ Opt_codepage,
+ Opt_charset,
+ Opt_namecase,
+ Opt_debug,
+ Opt_err_cont,
+ Opt_err_panic,
+ Opt_err_ro,
+ Opt_utf8_hack,
+ Opt_err,
+#ifdef CONFIG_EXFAT_DISCARD
+ Opt_discard,
+#endif /* EXFAT_CONFIG_DISCARD */
+};
+
+static const match_table_t exfat_tokens = {
+ {Opt_uid, "uid=%u"},
+ {Opt_gid, "gid=%u"},
+ {Opt_umask, "umask=%o"},
+ {Opt_dmask, "dmask=%o"},
+ {Opt_fmask, "fmask=%o"},
+ {Opt_allow_utime, "allow_utime=%o"},
+ {Opt_codepage, "codepage=%u"},
+ {Opt_charset, "iocharset=%s"},
+ {Opt_namecase, "namecase=%u"},
+ {Opt_debug, "debug"},
+ {Opt_err_cont, "errors=continue"},
+ {Opt_err_panic, "errors=panic"},
+ {Opt_err_ro, "errors=remount-ro"},
+ {Opt_utf8_hack, "utf8"},
+#ifdef CONFIG_EXFAT_DISCARD
+ {Opt_discard, "discard"},
+#endif /* CONFIG_EXFAT_DISCARD */
+ {Opt_err, NULL}
+};
+
+static int parse_options(char *options, int silent, int *debug,
+ struct exfat_mount_options *opts)
+{
+ char *p;
+ substring_t args[MAX_OPT_ARGS];
+ int option;
+ char *iocharset;
+
+ opts->fs_uid = current_uid();
+ opts->fs_gid = current_gid();
+ opts->fs_fmask = opts->fs_dmask = current->fs->umask;
+ opts->allow_utime = (unsigned short) -1;
+ opts->codepage = exfat_default_codepage;
+ opts->iocharset = exfat_default_iocharset;
+ opts->casesensitive = 0;
+ opts->errors = EXFAT_ERRORS_RO;
+#ifdef CONFIG_EXFAT_DISCARD
+ opts->discard = 0;
+#endif
+ *debug = 0;
+
+ if (!options)
+ goto out;
+
+ while ((p = strsep(&options, ",")) != NULL) {
+ int token;
+
+ if (!*p)
+ continue;
+
+ token = match_token(p, exfat_tokens, args);
+ switch (token) {
+ case Opt_uid:
+ if (match_int(&args[0], &option))
+ return 0;
+ opts->fs_uid = KUIDT_INIT(option);
+ break;
+ case Opt_gid:
+ if (match_int(&args[0], &option))
+ return 0;
+ opts->fs_gid = KGIDT_INIT(option);
+ break;
+ case Opt_umask:
+ case Opt_dmask:
+ case Opt_fmask:
+ if (match_octal(&args[0], &option))
+ return 0;
+ if (token != Opt_dmask)
+ opts->fs_fmask = option;
+ if (token != Opt_fmask)
+ opts->fs_dmask = option;
+ break;
+ case Opt_allow_utime:
+ if (match_octal(&args[0], &option))
+ return 0;
+ opts->allow_utime = option & 0022;
+ break;
+ case Opt_codepage:
+ if (match_int(&args[0], &option))
+ return 0;
+ opts->codepage = option;
+ break;
+ case Opt_charset:
+ if (opts->iocharset != exfat_default_iocharset)
+ kfree(opts->iocharset);
+ iocharset = match_strdup(&args[0]);
+ if (!iocharset)
+ return -ENOMEM;
+ opts->iocharset = iocharset;
+ break;
+ case Opt_namecase:
+ if (match_int(&args[0], &option))
+ return 0;
+ opts->casesensitive = option;
+ break;
+ case Opt_err_cont:
+ opts->errors = EXFAT_ERRORS_CONT;
+ break;
+ case Opt_err_panic:
+ opts->errors = EXFAT_ERRORS_PANIC;
+ break;
+ case Opt_err_ro:
+ opts->errors = EXFAT_ERRORS_RO;
+ break;
+ case Opt_debug:
+ *debug = 1;
+ break;
+#ifdef CONFIG_EXFAT_DISCARD
+ case Opt_discard:
+ opts->discard = 1;
+ break;
+#endif /* CONFIG_EXFAT_DISCARD */
+ case Opt_utf8_hack:
+ break;
+ default:
+ if (!silent)
+ pr_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 & 0022;
+
+ 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 fs_info_t *p_fs = &(sbi->fs_info);
+ struct dir_entry_t info;
+
+ EXFAT_I(inode)->fid.dir.dir = p_fs->root_dir;
+ EXFAT_I(inode)->fid.dir.flags = 0x01;
+ EXFAT_I(inode)->fid.entry = -1;
+ EXFAT_I(inode)->fid.start_clu = p_fs->root_dir;
+ EXFAT_I(inode)->fid.flags = 0x01;
+ EXFAT_I(inode)->fid.type = TYPE_DIR;
+ EXFAT_I(inode)->fid.rwoffset = 0;
+ EXFAT_I(inode)->fid.hint_last_off = -1;
+
+ EXFAT_I(inode)->target = NULL;
+
+ ffsReadStat(inode, &info);
+
+ inode->i_uid = sbi->options.fs_uid;
+ inode->i_gid = sbi->options.fs_gid;
+ INC_IVERSION(inode);
+ inode->i_generation = 0;
+ inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, 0777);
+ inode->i_op = &exfat_dir_inode_operations;
+ inode->i_fop = &exfat_dir_operations;
+
+ i_size_write(inode, info.Size);
+ inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1))
+ & ~((loff_t)p_fs->cluster_size - 1)) >> 9;
+ EXFAT_I(inode)->i_pos = ((loff_t) p_fs->root_dir << 32) | 0xffffffff;
+ EXFAT_I(inode)->mmu_private = i_size_read(inode);
+
+ exfat_save_attr(inode, ATTR_SUBDIR);
+ inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+ set_nlink(inode, info.NumSubdirs + 2);
+
+ return 0;
+}
+
+static void setup_dops(struct super_block *sb)
+{
+ if (EXFAT_SB(sb)->options.casesensitive == 0)
+ sb->s_d_op = &exfat_ci_dentry_ops;
+ else
+ sb->s_d_op = &exfat_dentry_ops;
+}
+
+static int exfat_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct inode *root_inode = NULL;
+ struct exfat_sb_info *sbi;
+ int debug, ret;
+ long error;
+ char buf[50];
+
+ /*
+ * GFP_KERNEL is ok here, because while we do hold the
+ * supeblock lock, memory pressure can't call back into
+ * the filesystem, since we're only just about to mount
+ * it and have no inodes etc active!
+ */
+ sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL);
+ if (!sbi)
+ return -ENOMEM;
+ mutex_init(&sbi->s_lock);
+ sb->s_fs_info = sbi;
+ sb->s_flags |= SB_NODIRATIME;
+ sb->s_magic = EXFAT_SUPER_MAGIC;
+ sb->s_op = &exfat_sops;
+ sb->s_export_op = &exfat_export_ops;
+
+ error = parse_options(data, silent, &debug, &sbi->options);
+ if (error)
+ goto out_fail;
+
+ setup_dops(sb);
+
+ error = -EIO;
+ sb_min_blocksize(sb, 512);
+ sb->s_maxbytes = 0x7fffffffffffffffLL; /* maximum file size */
+
+ ret = ffsMountVol(sb);
+ if (ret) {
+ if (!silent)
+ pr_err("[EXFAT] ffsMountVol failed\n");
+
+ goto out_fail;
+ }
+
+ /* set up enough so that it can read an inode */
+ exfat_hash_init(sb);
+
+ /*
+ * The low byte of FAT's first entry must have same value with
+ * media-field. But in real world, too many devices is
+ * writing wrong value. So, removed that validity check.
+ *
+ * if (FAT_FIRST_ENT(sb, media) != first)
+ */
+
+ /* codepage is not meaningful in exfat */
+ if (sbi->fs_info.vol_type != EXFAT) {
+ error = -EINVAL;
+ sprintf(buf, "cp%d", sbi->options.codepage);
+ sbi->nls_disk = load_nls(buf);
+ if (!sbi->nls_disk) {
+ pr_err("[EXFAT] Codepage %s not found\n", buf);
+ goto out_fail2;
+ }
+ }
+
+ sbi->nls_io = load_nls(sbi->options.iocharset);
+
+ error = -ENOMEM;
+ root_inode = new_inode(sb);
+ if (!root_inode)
+ goto out_fail2;
+ root_inode->i_ino = EXFAT_ROOT_INO;
+ SET_IVERSION(root_inode, 1);
+
+ error = exfat_read_root(root_inode);
+ if (error < 0)
+ goto out_fail2;
+ error = -ENOMEM;
+ exfat_attach(root_inode, EXFAT_I(root_inode)->i_pos);
+ insert_inode_hash(root_inode);
+ sb->s_root = d_make_root(root_inode);
+ if (!sb->s_root) {
+ pr_err("[EXFAT] Getting the root inode failed\n");
+ goto out_fail2;
+ }
+
+ return 0;
+
+out_fail2:
+ ffsUmountVol(sb);
+out_fail:
+ if (root_inode)
+ iput(root_inode);
+ sb->s_fs_info = NULL;
+ exfat_free_super(sbi);
+ return error;
+}
+
+static struct dentry *exfat_fs_mount(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data)
+{
+ return mount_bdev(fs_type, flags, dev_name, data, exfat_fill_super);
+}
+
+static void init_once(void *foo)
+{
+ struct exfat_inode_info *ei = (struct exfat_inode_info *)foo;
+
+ INIT_HLIST_NODE(&ei->i_hash_fat);
+ inode_init_once(&ei->vfs_inode);
+}
+
+static int __init exfat_init_inodecache(void)
+{
+ exfat_inode_cachep = kmem_cache_create("exfat_inode_cache",
+ sizeof(struct exfat_inode_info),
+ 0,
+ (SLAB_RECLAIM_ACCOUNT |
+ SLAB_MEM_SPREAD),
+ init_once);
+ if (exfat_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void __exit exfat_destroy_inodecache(void)
+{
+ /*
+ * Make sure all delayed rcu free inodes are flushed before we
+ * destroy cache.
+ */
+ rcu_barrier();
+ kmem_cache_destroy(exfat_inode_cachep);
+}
+
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+static void exfat_debug_kill_sb(struct super_block *sb)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ struct block_device *bdev = sb->s_bdev;
+ struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
+
+ 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.
+ */
+ down(&p_fs->v_sem);
+ FAT_release_all(sb);
+ buf_release_all(sb);
+ up(&p_fs->v_sem);
+
+ invalidate_bdev(bdev);
+ }
+ }
+
+ kill_block_super(sb);
+}
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+
+static struct file_system_type exfat_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "exfat",
+ .mount = exfat_fs_mount,
+#ifdef CONFIG_EXFAT_KERNEL_DEBUG
+ .kill_sb = exfat_debug_kill_sb,
+#else
+ .kill_sb = kill_block_super,
+#endif /* CONFIG_EXFAT_KERNEL_DEBUG */
+ .fs_flags = FS_REQUIRES_DEV,
+};
+
+static int __init init_exfat(void)
+{
+ int err;
+
+ BUILD_BUG_ON(sizeof(struct dentry_t) != DENTRY_SIZE);
+ BUILD_BUG_ON(sizeof(struct dos_dentry_t) != DENTRY_SIZE);
+ BUILD_BUG_ON(sizeof(struct ext_dentry_t) != DENTRY_SIZE);
+ BUILD_BUG_ON(sizeof(struct file_dentry_t) != DENTRY_SIZE);
+ BUILD_BUG_ON(sizeof(struct strm_dentry_t) != DENTRY_SIZE);
+ BUILD_BUG_ON(sizeof(struct name_dentry_t) != DENTRY_SIZE);
+ BUILD_BUG_ON(sizeof(struct bmap_dentry_t) != DENTRY_SIZE);
+ BUILD_BUG_ON(sizeof(struct case_dentry_t) != DENTRY_SIZE);
+ BUILD_BUG_ON(sizeof(struct volm_dentry_t) != DENTRY_SIZE);
+
+ pr_info("exFAT: Version %s\n", EXFAT_VERSION);
+
+ err = exfat_init_inodecache();
+ if (err)
+ return err;
+
+ err = register_filesystem(&exfat_fs_type);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static void __exit exit_exfat(void)
+{
+ exfat_destroy_inodecache();
+ unregister_filesystem(&exfat_fs_type);
+}
+
+module_init(init_exfat);
+module_exit(exit_exfat);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("exFAT Filesystem Driver");
+MODULE_ALIAS_FS("exfat");
diff --git a/drivers/staging/exfat/exfat_upcase.c b/drivers/staging/exfat/exfat_upcase.c
new file mode 100644
index 000000000000..366082fb3dab
--- /dev/null
+++ b/drivers/staging/exfat/exfat_upcase.c
@@ -0,0 +1,740 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/types.h>
+#include "exfat.h"
+
+const u8 uni_upcase[NUM_UPCASE << 1] = {
+ 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
+ 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00,
+ 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x00,
+ 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0F, 0x00,
+ 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00,
+ 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00,
+ 0x18, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, 0x00,
+ 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x1F, 0x00,
+ 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00,
+ 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00,
+ 0x28, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B, 0x00,
+ 0x2C, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x2F, 0x00,
+ 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00,
+ 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00,
+ 0x38, 0x00, 0x39, 0x00, 0x3A, 0x00, 0x3B, 0x00,
+ 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F, 0x00,
+ 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00,
+ 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
+ 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00,
+ 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00,
+ 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00,
+ 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00,
+ 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x5B, 0x00,
+ 0x5C, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x5F, 0x00,
+ 0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00,
+ 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
+ 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00,
+ 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00,
+ 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00,
+ 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00,
+ 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x7B, 0x00,
+ 0x7C, 0x00, 0x7D, 0x00, 0x7E, 0x00, 0x7F, 0x00,
+ 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00,
+ 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00,
+ 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00,
+ 0x8C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00,
+ 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00,
+ 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00,
+ 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00,
+ 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00,
+ 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00,
+ 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, 0x00,
+ 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, 0x00,
+ 0xAC, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAF, 0x00,
+ 0xB0, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00,
+ 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB7, 0x00,
+ 0xB8, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBB, 0x00,
+ 0xBC, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBF, 0x00,
+ 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00,
+ 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00,
+ 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00,
+ 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00,
+ 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00,
+ 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD7, 0x00,
+ 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00,
+ 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0xDF, 0x00,
+ 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00,
+ 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00,
+ 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00,
+ 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00,
+ 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00,
+ 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xF7, 0x00,
+ 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00,
+ 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0x78, 0x01,
+ 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01,
+ 0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01,
+ 0x08, 0x01, 0x08, 0x01, 0x0A, 0x01, 0x0A, 0x01,
+ 0x0C, 0x01, 0x0C, 0x01, 0x0E, 0x01, 0x0E, 0x01,
+ 0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01,
+ 0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01,
+ 0x18, 0x01, 0x18, 0x01, 0x1A, 0x01, 0x1A, 0x01,
+ 0x1C, 0x01, 0x1C, 0x01, 0x1E, 0x01, 0x1E, 0x01,
+ 0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01,
+ 0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01,
+ 0x28, 0x01, 0x28, 0x01, 0x2A, 0x01, 0x2A, 0x01,
+ 0x2C, 0x01, 0x2C, 0x01, 0x2E, 0x01, 0x2E, 0x01,
+ 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01,
+ 0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01,
+ 0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3B, 0x01,
+ 0x3B, 0x01, 0x3D, 0x01, 0x3D, 0x01, 0x3F, 0x01,
+ 0x3F, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01,
+ 0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01,
+ 0x47, 0x01, 0x49, 0x01, 0x4A, 0x01, 0x4A, 0x01,
+ 0x4C, 0x01, 0x4C, 0x01, 0x4E, 0x01, 0x4E, 0x01,
+ 0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01,
+ 0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01,
+ 0x58, 0x01, 0x58, 0x01, 0x5A, 0x01, 0x5A, 0x01,
+ 0x5C, 0x01, 0x5C, 0x01, 0x5E, 0x01, 0x5E, 0x01,
+ 0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01,
+ 0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01,
+ 0x68, 0x01, 0x68, 0x01, 0x6A, 0x01, 0x6A, 0x01,
+ 0x6C, 0x01, 0x6C, 0x01, 0x6E, 0x01, 0x6E, 0x01,
+ 0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01,
+ 0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01,
+ 0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7B, 0x01,
+ 0x7B, 0x01, 0x7D, 0x01, 0x7D, 0x01, 0x7F, 0x01,
+ 0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01,
+ 0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01,
+ 0x87, 0x01, 0x89, 0x01, 0x8A, 0x01, 0x8B, 0x01,
+ 0x8B, 0x01, 0x8D, 0x01, 0x8E, 0x01, 0x8F, 0x01,
+ 0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01,
+ 0x94, 0x01, 0xF6, 0x01, 0x96, 0x01, 0x97, 0x01,
+ 0x98, 0x01, 0x98, 0x01, 0x3D, 0x02, 0x9B, 0x01,
+ 0x9C, 0x01, 0x9D, 0x01, 0x20, 0x02, 0x9F, 0x01,
+ 0xA0, 0x01, 0xA0, 0x01, 0xA2, 0x01, 0xA2, 0x01,
+ 0xA4, 0x01, 0xA4, 0x01, 0xA6, 0x01, 0xA7, 0x01,
+ 0xA7, 0x01, 0xA9, 0x01, 0xAA, 0x01, 0xAB, 0x01,
+ 0xAC, 0x01, 0xAC, 0x01, 0xAE, 0x01, 0xAF, 0x01,
+ 0xAF, 0x01, 0xB1, 0x01, 0xB2, 0x01, 0xB3, 0x01,
+ 0xB3, 0x01, 0xB5, 0x01, 0xB5, 0x01, 0xB7, 0x01,
+ 0xB8, 0x01, 0xB8, 0x01, 0xBA, 0x01, 0xBB, 0x01,
+ 0xBC, 0x01, 0xBC, 0x01, 0xBE, 0x01, 0xF7, 0x01,
+ 0xC0, 0x01, 0xC1, 0x01, 0xC2, 0x01, 0xC3, 0x01,
+ 0xC4, 0x01, 0xC5, 0x01, 0xC4, 0x01, 0xC7, 0x01,
+ 0xC8, 0x01, 0xC7, 0x01, 0xCA, 0x01, 0xCB, 0x01,
+ 0xCA, 0x01, 0xCD, 0x01, 0xCD, 0x01, 0xCF, 0x01,
+ 0xCF, 0x01, 0xD1, 0x01, 0xD1, 0x01, 0xD3, 0x01,
+ 0xD3, 0x01, 0xD5, 0x01, 0xD5, 0x01, 0xD7, 0x01,
+ 0xD7, 0x01, 0xD9, 0x01, 0xD9, 0x01, 0xDB, 0x01,
+ 0xDB, 0x01, 0x8E, 0x01, 0xDE, 0x01, 0xDE, 0x01,
+ 0xE0, 0x01, 0xE0, 0x01, 0xE2, 0x01, 0xE2, 0x01,
+ 0xE4, 0x01, 0xE4, 0x01, 0xE6, 0x01, 0xE6, 0x01,
+ 0xE8, 0x01, 0xE8, 0x01, 0xEA, 0x01, 0xEA, 0x01,
+ 0xEC, 0x01, 0xEC, 0x01, 0xEE, 0x01, 0xEE, 0x01,
+ 0xF0, 0x01, 0xF1, 0x01, 0xF2, 0x01, 0xF1, 0x01,
+ 0xF4, 0x01, 0xF4, 0x01, 0xF6, 0x01, 0xF7, 0x01,
+ 0xF8, 0x01, 0xF8, 0x01, 0xFA, 0x01, 0xFA, 0x01,
+ 0xFC, 0x01, 0xFC, 0x01, 0xFE, 0x01, 0xFE, 0x01,
+ 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02,
+ 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x0A, 0x02,
+ 0x0C, 0x02, 0x0C, 0x02, 0x0E, 0x02, 0x0E, 0x02,
+ 0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02,
+ 0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02,
+ 0x18, 0x02, 0x18, 0x02, 0x1A, 0x02, 0x1A, 0x02,
+ 0x1C, 0x02, 0x1C, 0x02, 0x1E, 0x02, 0x1E, 0x02,
+ 0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02,
+ 0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02,
+ 0x28, 0x02, 0x28, 0x02, 0x2A, 0x02, 0x2A, 0x02,
+ 0x2C, 0x02, 0x2C, 0x02, 0x2E, 0x02, 0x2E, 0x02,
+ 0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02,
+ 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02,
+ 0x38, 0x02, 0x39, 0x02, 0x65, 0x2C, 0x3B, 0x02,
+ 0x3B, 0x02, 0x3D, 0x02, 0x66, 0x2C, 0x3F, 0x02,
+ 0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02,
+ 0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02,
+ 0x48, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x4A, 0x02,
+ 0x4C, 0x02, 0x4C, 0x02, 0x4E, 0x02, 0x4E, 0x02,
+ 0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01,
+ 0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8A, 0x01,
+ 0x58, 0x02, 0x8F, 0x01, 0x5A, 0x02, 0x90, 0x01,
+ 0x5C, 0x02, 0x5D, 0x02, 0x5E, 0x02, 0x5F, 0x02,
+ 0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01,
+ 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02,
+ 0x97, 0x01, 0x96, 0x01, 0x6A, 0x02, 0x62, 0x2C,
+ 0x6C, 0x02, 0x6D, 0x02, 0x6E, 0x02, 0x9C, 0x01,
+ 0x70, 0x02, 0x71, 0x02, 0x9D, 0x01, 0x73, 0x02,
+ 0x74, 0x02, 0x9F, 0x01, 0x76, 0x02, 0x77, 0x02,
+ 0x78, 0x02, 0x79, 0x02, 0x7A, 0x02, 0x7B, 0x02,
+ 0x7C, 0x02, 0x64, 0x2C, 0x7E, 0x02, 0x7F, 0x02,
+ 0xA6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xA9, 0x01,
+ 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02,
+ 0xAE, 0x01, 0x44, 0x02, 0xB1, 0x01, 0xB2, 0x01,
+ 0x45, 0x02, 0x8D, 0x02, 0x8E, 0x02, 0x8F, 0x02,
+ 0x90, 0x02, 0x91, 0x02, 0xB7, 0x01, 0x93, 0x02,
+ 0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02,
+ 0x98, 0x02, 0x99, 0x02, 0x9A, 0x02, 0x9B, 0x02,
+ 0x9C, 0x02, 0x9D, 0x02, 0x9E, 0x02, 0x9F, 0x02,
+ 0xA0, 0x02, 0xA1, 0x02, 0xA2, 0x02, 0xA3, 0x02,
+ 0xA4, 0x02, 0xA5, 0x02, 0xA6, 0x02, 0xA7, 0x02,
+ 0xA8, 0x02, 0xA9, 0x02, 0xAA, 0x02, 0xAB, 0x02,
+ 0xAC, 0x02, 0xAD, 0x02, 0xAE, 0x02, 0xAF, 0x02,
+ 0xB0, 0x02, 0xB1, 0x02, 0xB2, 0x02, 0xB3, 0x02,
+ 0xB4, 0x02, 0xB5, 0x02, 0xB6, 0x02, 0xB7, 0x02,
+ 0xB8, 0x02, 0xB9, 0x02, 0xBA, 0x02, 0xBB, 0x02,
+ 0xBC, 0x02, 0xBD, 0x02, 0xBE, 0x02, 0xBF, 0x02,
+ 0xC0, 0x02, 0xC1, 0x02, 0xC2, 0x02, 0xC3, 0x02,
+ 0xC4, 0x02, 0xC5, 0x02, 0xC6, 0x02, 0xC7, 0x02,
+ 0xC8, 0x02, 0xC9, 0x02, 0xCA, 0x02, 0xCB, 0x02,
+ 0xCC, 0x02, 0xCD, 0x02, 0xCE, 0x02, 0xCF, 0x02,
+ 0xD0, 0x02, 0xD1, 0x02, 0xD2, 0x02, 0xD3, 0x02,
+ 0xD4, 0x02, 0xD5, 0x02, 0xD6, 0x02, 0xD7, 0x02,
+ 0xD8, 0x02, 0xD9, 0x02, 0xDA, 0x02, 0xDB, 0x02,
+ 0xDC, 0x02, 0xDD, 0x02, 0xDE, 0x02, 0xDF, 0x02,
+ 0xE0, 0x02, 0xE1, 0x02, 0xE2, 0x02, 0xE3, 0x02,
+ 0xE4, 0x02, 0xE5, 0x02, 0xE6, 0x02, 0xE7, 0x02,
+ 0xE8, 0x02, 0xE9, 0x02, 0xEA, 0x02, 0xEB, 0x02,
+ 0xEC, 0x02, 0xED, 0x02, 0xEE, 0x02, 0xEF, 0x02,
+ 0xF0, 0x02, 0xF1, 0x02, 0xF2, 0x02, 0xF3, 0x02,
+ 0xF4, 0x02, 0xF5, 0x02, 0xF6, 0x02, 0xF7, 0x02,
+ 0xF8, 0x02, 0xF9, 0x02, 0xFA, 0x02, 0xFB, 0x02,
+ 0xFC, 0x02, 0xFD, 0x02, 0xFE, 0x02, 0xFF, 0x02,
+ 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03,
+ 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03,
+ 0x08, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0B, 0x03,
+ 0x0C, 0x03, 0x0D, 0x03, 0x0E, 0x03, 0x0F, 0x03,
+ 0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03,
+ 0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03,
+ 0x18, 0x03, 0x19, 0x03, 0x1A, 0x03, 0x1B, 0x03,
+ 0x1C, 0x03, 0x1D, 0x03, 0x1E, 0x03, 0x1F, 0x03,
+ 0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03,
+ 0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03,
+ 0x28, 0x03, 0x29, 0x03, 0x2A, 0x03, 0x2B, 0x03,
+ 0x2C, 0x03, 0x2D, 0x03, 0x2E, 0x03, 0x2F, 0x03,
+ 0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03,
+ 0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03,
+ 0x38, 0x03, 0x39, 0x03, 0x3A, 0x03, 0x3B, 0x03,
+ 0x3C, 0x03, 0x3D, 0x03, 0x3E, 0x03, 0x3F, 0x03,
+ 0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03,
+ 0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03,
+ 0x48, 0x03, 0x49, 0x03, 0x4A, 0x03, 0x4B, 0x03,
+ 0x4C, 0x03, 0x4D, 0x03, 0x4E, 0x03, 0x4F, 0x03,
+ 0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03,
+ 0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03,
+ 0x58, 0x03, 0x59, 0x03, 0x5A, 0x03, 0x5B, 0x03,
+ 0x5C, 0x03, 0x5D, 0x03, 0x5E, 0x03, 0x5F, 0x03,
+ 0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03,
+ 0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03,
+ 0x68, 0x03, 0x69, 0x03, 0x6A, 0x03, 0x6B, 0x03,
+ 0x6C, 0x03, 0x6D, 0x03, 0x6E, 0x03, 0x6F, 0x03,
+ 0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03,
+ 0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03,
+ 0x78, 0x03, 0x79, 0x03, 0x7A, 0x03, 0xFD, 0x03,
+ 0xFE, 0x03, 0xFF, 0x03, 0x7E, 0x03, 0x7F, 0x03,
+ 0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03,
+ 0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03,
+ 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, 0x8B, 0x03,
+ 0x8C, 0x03, 0x8D, 0x03, 0x8E, 0x03, 0x8F, 0x03,
+ 0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03,
+ 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03,
+ 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03,
+ 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03,
+ 0xA0, 0x03, 0xA1, 0x03, 0xA2, 0x03, 0xA3, 0x03,
+ 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03,
+ 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03,
+ 0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03,
+ 0xB0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03,
+ 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03,
+ 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03,
+ 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03,
+ 0xA0, 0x03, 0xA1, 0x03, 0xA3, 0x03, 0xA3, 0x03,
+ 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03,
+ 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03,
+ 0x8C, 0x03, 0x8E, 0x03, 0x8F, 0x03, 0xCF, 0x03,
+ 0xD0, 0x03, 0xD1, 0x03, 0xD2, 0x03, 0xD3, 0x03,
+ 0xD4, 0x03, 0xD5, 0x03, 0xD6, 0x03, 0xD7, 0x03,
+ 0xD8, 0x03, 0xD8, 0x03, 0xDA, 0x03, 0xDA, 0x03,
+ 0xDC, 0x03, 0xDC, 0x03, 0xDE, 0x03, 0xDE, 0x03,
+ 0xE0, 0x03, 0xE0, 0x03, 0xE2, 0x03, 0xE2, 0x03,
+ 0xE4, 0x03, 0xE4, 0x03, 0xE6, 0x03, 0xE6, 0x03,
+ 0xE8, 0x03, 0xE8, 0x03, 0xEA, 0x03, 0xEA, 0x03,
+ 0xEC, 0x03, 0xEC, 0x03, 0xEE, 0x03, 0xEE, 0x03,
+ 0xF0, 0x03, 0xF1, 0x03, 0xF9, 0x03, 0xF3, 0x03,
+ 0xF4, 0x03, 0xF5, 0x03, 0xF6, 0x03, 0xF7, 0x03,
+ 0xF7, 0x03, 0xF9, 0x03, 0xFA, 0x03, 0xFA, 0x03,
+ 0xFC, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03,
+ 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04,
+ 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04,
+ 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04,
+ 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04,
+ 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04,
+ 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04,
+ 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04,
+ 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04,
+ 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04,
+ 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04,
+ 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04,
+ 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04,
+ 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04,
+ 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04,
+ 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04,
+ 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04,
+ 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04,
+ 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04,
+ 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04,
+ 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04,
+ 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04,
+ 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04,
+ 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04,
+ 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04,
+ 0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04,
+ 0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04,
+ 0x68, 0x04, 0x68, 0x04, 0x6A, 0x04, 0x6A, 0x04,
+ 0x6C, 0x04, 0x6C, 0x04, 0x6E, 0x04, 0x6E, 0x04,
+ 0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04,
+ 0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04,
+ 0x78, 0x04, 0x78, 0x04, 0x7A, 0x04, 0x7A, 0x04,
+ 0x7C, 0x04, 0x7C, 0x04, 0x7E, 0x04, 0x7E, 0x04,
+ 0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04,
+ 0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04,
+ 0x88, 0x04, 0x89, 0x04, 0x8A, 0x04, 0x8A, 0x04,
+ 0x8C, 0x04, 0x8C, 0x04, 0x8E, 0x04, 0x8E, 0x04,
+ 0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04,
+ 0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04,
+ 0x98, 0x04, 0x98, 0x04, 0x9A, 0x04, 0x9A, 0x04,
+ 0x9C, 0x04, 0x9C, 0x04, 0x9E, 0x04, 0x9E, 0x04,
+ 0xA0, 0x04, 0xA0, 0x04, 0xA2, 0x04, 0xA2, 0x04,
+ 0xA4, 0x04, 0xA4, 0x04, 0xA6, 0x04, 0xA6, 0x04,
+ 0xA8, 0x04, 0xA8, 0x04, 0xAA, 0x04, 0xAA, 0x04,
+ 0xAC, 0x04, 0xAC, 0x04, 0xAE, 0x04, 0xAE, 0x04,
+ 0xB0, 0x04, 0xB0, 0x04, 0xB2, 0x04, 0xB2, 0x04,
+ 0xB4, 0x04, 0xB4, 0x04, 0xB6, 0x04, 0xB6, 0x04,
+ 0xB8, 0x04, 0xB8, 0x04, 0xBA, 0x04, 0xBA, 0x04,
+ 0xBC, 0x04, 0xBC, 0x04, 0xBE, 0x04, 0xBE, 0x04,
+ 0xC0, 0x04, 0xC1, 0x04, 0xC1, 0x04, 0xC3, 0x04,
+ 0xC3, 0x04, 0xC5, 0x04, 0xC5, 0x04, 0xC7, 0x04,
+ 0xC7, 0x04, 0xC9, 0x04, 0xC9, 0x04, 0xCB, 0x04,
+ 0xCB, 0x04, 0xCD, 0x04, 0xCD, 0x04, 0xC0, 0x04,
+ 0xD0, 0x04, 0xD0, 0x04, 0xD2, 0x04, 0xD2, 0x04,
+ 0xD4, 0x04, 0xD4, 0x04, 0xD6, 0x04, 0xD6, 0x04,
+ 0xD8, 0x04, 0xD8, 0x04, 0xDA, 0x04, 0xDA, 0x04,
+ 0xDC, 0x04, 0xDC, 0x04, 0xDE, 0x04, 0xDE, 0x04,
+ 0xE0, 0x04, 0xE0, 0x04, 0xE2, 0x04, 0xE2, 0x04,
+ 0xE4, 0x04, 0xE4, 0x04, 0xE6, 0x04, 0xE6, 0x04,
+ 0xE8, 0x04, 0xE8, 0x04, 0xEA, 0x04, 0xEA, 0x04,
+ 0xEC, 0x04, 0xEC, 0x04, 0xEE, 0x04, 0xEE, 0x04,
+ 0xF0, 0x04, 0xF0, 0x04, 0xF2, 0x04, 0xF2, 0x04,
+ 0xF4, 0x04, 0xF4, 0x04, 0xF6, 0x04, 0xF6, 0x04,
+ 0xF8, 0x04, 0xF8, 0x04, 0xFA, 0x04, 0xFA, 0x04,
+ 0xFC, 0x04, 0xFC, 0x04, 0xFE, 0x04, 0xFE, 0x04,
+ 0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05,
+ 0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05,
+ 0x08, 0x05, 0x08, 0x05, 0x0A, 0x05, 0x0A, 0x05,
+ 0x0C, 0x05, 0x0C, 0x05, 0x0E, 0x05, 0x0E, 0x05,
+ 0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05,
+ 0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05,
+ 0x18, 0x05, 0x19, 0x05, 0x1A, 0x05, 0x1B, 0x05,
+ 0x1C, 0x05, 0x1D, 0x05, 0x1E, 0x05, 0x1F, 0x05,
+ 0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05,
+ 0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05,
+ 0x28, 0x05, 0x29, 0x05, 0x2A, 0x05, 0x2B, 0x05,
+ 0x2C, 0x05, 0x2D, 0x05, 0x2E, 0x05, 0x2F, 0x05,
+ 0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05,
+ 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05,
+ 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05,
+ 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05,
+ 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05,
+ 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05,
+ 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05,
+ 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05,
+ 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05,
+ 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05,
+ 0x58, 0x05, 0x59, 0x05, 0x5A, 0x05, 0x5B, 0x05,
+ 0x5C, 0x05, 0x5D, 0x05, 0x5E, 0x05, 0x5F, 0x05,
+ 0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05,
+ 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05,
+ 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05,
+ 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05,
+ 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05,
+ 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05,
+ 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05,
+ 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05,
+ 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05,
+ 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xFF, 0xFF,
+ 0xF6, 0x17, 0x63, 0x2C, 0x7E, 0x1D, 0x7F, 0x1D,
+ 0x80, 0x1D, 0x81, 0x1D, 0x82, 0x1D, 0x83, 0x1D,
+ 0x84, 0x1D, 0x85, 0x1D, 0x86, 0x1D, 0x87, 0x1D,
+ 0x88, 0x1D, 0x89, 0x1D, 0x8A, 0x1D, 0x8B, 0x1D,
+ 0x8C, 0x1D, 0x8D, 0x1D, 0x8E, 0x1D, 0x8F, 0x1D,
+ 0x90, 0x1D, 0x91, 0x1D, 0x92, 0x1D, 0x93, 0x1D,
+ 0x94, 0x1D, 0x95, 0x1D, 0x96, 0x1D, 0x97, 0x1D,
+ 0x98, 0x1D, 0x99, 0x1D, 0x9A, 0x1D, 0x9B, 0x1D,
+ 0x9C, 0x1D, 0x9D, 0x1D, 0x9E, 0x1D, 0x9F, 0x1D,
+ 0xA0, 0x1D, 0xA1, 0x1D, 0xA2, 0x1D, 0xA3, 0x1D,
+ 0xA4, 0x1D, 0xA5, 0x1D, 0xA6, 0x1D, 0xA7, 0x1D,
+ 0xA8, 0x1D, 0xA9, 0x1D, 0xAA, 0x1D, 0xAB, 0x1D,
+ 0xAC, 0x1D, 0xAD, 0x1D, 0xAE, 0x1D, 0xAF, 0x1D,
+ 0xB0, 0x1D, 0xB1, 0x1D, 0xB2, 0x1D, 0xB3, 0x1D,
+ 0xB4, 0x1D, 0xB5, 0x1D, 0xB6, 0x1D, 0xB7, 0x1D,
+ 0xB8, 0x1D, 0xB9, 0x1D, 0xBA, 0x1D, 0xBB, 0x1D,
+ 0xBC, 0x1D, 0xBD, 0x1D, 0xBE, 0x1D, 0xBF, 0x1D,
+ 0xC0, 0x1D, 0xC1, 0x1D, 0xC2, 0x1D, 0xC3, 0x1D,
+ 0xC4, 0x1D, 0xC5, 0x1D, 0xC6, 0x1D, 0xC7, 0x1D,
+ 0xC8, 0x1D, 0xC9, 0x1D, 0xCA, 0x1D, 0xCB, 0x1D,
+ 0xCC, 0x1D, 0xCD, 0x1D, 0xCE, 0x1D, 0xCF, 0x1D,
+ 0xD0, 0x1D, 0xD1, 0x1D, 0xD2, 0x1D, 0xD3, 0x1D,
+ 0xD4, 0x1D, 0xD5, 0x1D, 0xD6, 0x1D, 0xD7, 0x1D,
+ 0xD8, 0x1D, 0xD9, 0x1D, 0xDA, 0x1D, 0xDB, 0x1D,
+ 0xDC, 0x1D, 0xDD, 0x1D, 0xDE, 0x1D, 0xDF, 0x1D,
+ 0xE0, 0x1D, 0xE1, 0x1D, 0xE2, 0x1D, 0xE3, 0x1D,
+ 0xE4, 0x1D, 0xE5, 0x1D, 0xE6, 0x1D, 0xE7, 0x1D,
+ 0xE8, 0x1D, 0xE9, 0x1D, 0xEA, 0x1D, 0xEB, 0x1D,
+ 0xEC, 0x1D, 0xED, 0x1D, 0xEE, 0x1D, 0xEF, 0x1D,
+ 0xF0, 0x1D, 0xF1, 0x1D, 0xF2, 0x1D, 0xF3, 0x1D,
+ 0xF4, 0x1D, 0xF5, 0x1D, 0xF6, 0x1D, 0xF7, 0x1D,
+ 0xF8, 0x1D, 0xF9, 0x1D, 0xFA, 0x1D, 0xFB, 0x1D,
+ 0xFC, 0x1D, 0xFD, 0x1D, 0xFE, 0x1D, 0xFF, 0x1D,
+ 0x00, 0x1E, 0x00, 0x1E, 0x02, 0x1E, 0x02, 0x1E,
+ 0x04, 0x1E, 0x04, 0x1E, 0x06, 0x1E, 0x06, 0x1E,
+ 0x08, 0x1E, 0x08, 0x1E, 0x0A, 0x1E, 0x0A, 0x1E,
+ 0x0C, 0x1E, 0x0C, 0x1E, 0x0E, 0x1E, 0x0E, 0x1E,
+ 0x10, 0x1E, 0x10, 0x1E, 0x12, 0x1E, 0x12, 0x1E,
+ 0x14, 0x1E, 0x14, 0x1E, 0x16, 0x1E, 0x16, 0x1E,
+ 0x18, 0x1E, 0x18, 0x1E, 0x1A, 0x1E, 0x1A, 0x1E,
+ 0x1C, 0x1E, 0x1C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
+ 0x20, 0x1E, 0x20, 0x1E, 0x22, 0x1E, 0x22, 0x1E,
+ 0x24, 0x1E, 0x24, 0x1E, 0x26, 0x1E, 0x26, 0x1E,
+ 0x28, 0x1E, 0x28, 0x1E, 0x2A, 0x1E, 0x2A, 0x1E,
+ 0x2C, 0x1E, 0x2C, 0x1E, 0x2E, 0x1E, 0x2E, 0x1E,
+ 0x30, 0x1E, 0x30, 0x1E, 0x32, 0x1E, 0x32, 0x1E,
+ 0x34, 0x1E, 0x34, 0x1E, 0x36, 0x1E, 0x36, 0x1E,
+ 0x38, 0x1E, 0x38, 0x1E, 0x3A, 0x1E, 0x3A, 0x1E,
+ 0x3C, 0x1E, 0x3C, 0x1E, 0x3E, 0x1E, 0x3E, 0x1E,
+ 0x40, 0x1E, 0x40, 0x1E, 0x42, 0x1E, 0x42, 0x1E,
+ 0x44, 0x1E, 0x44, 0x1E, 0x46, 0x1E, 0x46, 0x1E,
+ 0x48, 0x1E, 0x48, 0x1E, 0x4A, 0x1E, 0x4A, 0x1E,
+ 0x4C, 0x1E, 0x4C, 0x1E, 0x4E, 0x1E, 0x4E, 0x1E,
+ 0x50, 0x1E, 0x50, 0x1E, 0x52, 0x1E, 0x52, 0x1E,
+ 0x54, 0x1E, 0x54, 0x1E, 0x56, 0x1E, 0x56, 0x1E,
+ 0x58, 0x1E, 0x58, 0x1E, 0x5A, 0x1E, 0x5A, 0x1E,
+ 0x5C, 0x1E, 0x5C, 0x1E, 0x5E, 0x1E, 0x5E, 0x1E,
+ 0x60, 0x1E, 0x60, 0x1E, 0x62, 0x1E, 0x62, 0x1E,
+ 0x64, 0x1E, 0x64, 0x1E, 0x66, 0x1E, 0x66, 0x1E,
+ 0x68, 0x1E, 0x68, 0x1E, 0x6A, 0x1E, 0x6A, 0x1E,
+ 0x6C, 0x1E, 0x6C, 0x1E, 0x6E, 0x1E, 0x6E, 0x1E,
+ 0x70, 0x1E, 0x70, 0x1E, 0x72, 0x1E, 0x72, 0x1E,
+ 0x74, 0x1E, 0x74, 0x1E, 0x76, 0x1E, 0x76, 0x1E,
+ 0x78, 0x1E, 0x78, 0x1E, 0x7A, 0x1E, 0x7A, 0x1E,
+ 0x7C, 0x1E, 0x7C, 0x1E, 0x7E, 0x1E, 0x7E, 0x1E,
+ 0x80, 0x1E, 0x80, 0x1E, 0x82, 0x1E, 0x82, 0x1E,
+ 0x84, 0x1E, 0x84, 0x1E, 0x86, 0x1E, 0x86, 0x1E,
+ 0x88, 0x1E, 0x88, 0x1E, 0x8A, 0x1E, 0x8A, 0x1E,
+ 0x8C, 0x1E, 0x8C, 0x1E, 0x8E, 0x1E, 0x8E, 0x1E,
+ 0x90, 0x1E, 0x90, 0x1E, 0x92, 0x1E, 0x92, 0x1E,
+ 0x94, 0x1E, 0x94, 0x1E, 0x96, 0x1E, 0x97, 0x1E,
+ 0x98, 0x1E, 0x99, 0x1E, 0x9A, 0x1E, 0x9B, 0x1E,
+ 0x9C, 0x1E, 0x9D, 0x1E, 0x9E, 0x1E, 0x9F, 0x1E,
+ 0xA0, 0x1E, 0xA0, 0x1E, 0xA2, 0x1E, 0xA2, 0x1E,
+ 0xA4, 0x1E, 0xA4, 0x1E, 0xA6, 0x1E, 0xA6, 0x1E,
+ 0xA8, 0x1E, 0xA8, 0x1E, 0xAA, 0x1E, 0xAA, 0x1E,
+ 0xAC, 0x1E, 0xAC, 0x1E, 0xAE, 0x1E, 0xAE, 0x1E,
+ 0xB0, 0x1E, 0xB0, 0x1E, 0xB2, 0x1E, 0xB2, 0x1E,
+ 0xB4, 0x1E, 0xB4, 0x1E, 0xB6, 0x1E, 0xB6, 0x1E,
+ 0xB8, 0x1E, 0xB8, 0x1E, 0xBA, 0x1E, 0xBA, 0x1E,
+ 0xBC, 0x1E, 0xBC, 0x1E, 0xBE, 0x1E, 0xBE, 0x1E,
+ 0xC0, 0x1E, 0xC0, 0x1E, 0xC2, 0x1E, 0xC2, 0x1E,
+ 0xC4, 0x1E, 0xC4, 0x1E, 0xC6, 0x1E, 0xC6, 0x1E,
+ 0xC8, 0x1E, 0xC8, 0x1E, 0xCA, 0x1E, 0xCA, 0x1E,
+ 0xCC, 0x1E, 0xCC, 0x1E, 0xCE, 0x1E, 0xCE, 0x1E,
+ 0xD0, 0x1E, 0xD0, 0x1E, 0xD2, 0x1E, 0xD2, 0x1E,
+ 0xD4, 0x1E, 0xD4, 0x1E, 0xD6, 0x1E, 0xD6, 0x1E,
+ 0xD8, 0x1E, 0xD8, 0x1E, 0xDA, 0x1E, 0xDA, 0x1E,
+ 0xDC, 0x1E, 0xDC, 0x1E, 0xDE, 0x1E, 0xDE, 0x1E,
+ 0xE0, 0x1E, 0xE0, 0x1E, 0xE2, 0x1E, 0xE2, 0x1E,
+ 0xE4, 0x1E, 0xE4, 0x1E, 0xE6, 0x1E, 0xE6, 0x1E,
+ 0xE8, 0x1E, 0xE8, 0x1E, 0xEA, 0x1E, 0xEA, 0x1E,
+ 0xEC, 0x1E, 0xEC, 0x1E, 0xEE, 0x1E, 0xEE, 0x1E,
+ 0xF0, 0x1E, 0xF0, 0x1E, 0xF2, 0x1E, 0xF2, 0x1E,
+ 0xF4, 0x1E, 0xF4, 0x1E, 0xF6, 0x1E, 0xF6, 0x1E,
+ 0xF8, 0x1E, 0xF8, 0x1E, 0xFA, 0x1E, 0xFB, 0x1E,
+ 0xFC, 0x1E, 0xFD, 0x1E, 0xFE, 0x1E, 0xFF, 0x1E,
+ 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F,
+ 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F,
+ 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F,
+ 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F,
+ 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F,
+ 0x1C, 0x1F, 0x1D, 0x1F, 0x16, 0x1F, 0x17, 0x1F,
+ 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F,
+ 0x1C, 0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1F, 0x1F,
+ 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F,
+ 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F,
+ 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F,
+ 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F,
+ 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F,
+ 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F,
+ 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F,
+ 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F,
+ 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F,
+ 0x4C, 0x1F, 0x4D, 0x1F, 0x46, 0x1F, 0x47, 0x1F,
+ 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F,
+ 0x4C, 0x1F, 0x4D, 0x1F, 0x4E, 0x1F, 0x4F, 0x1F,
+ 0x50, 0x1F, 0x59, 0x1F, 0x52, 0x1F, 0x5B, 0x1F,
+ 0x54, 0x1F, 0x5D, 0x1F, 0x56, 0x1F, 0x5F, 0x1F,
+ 0x58, 0x1F, 0x59, 0x1F, 0x5A, 0x1F, 0x5B, 0x1F,
+ 0x5C, 0x1F, 0x5D, 0x1F, 0x5E, 0x1F, 0x5F, 0x1F,
+ 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F,
+ 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F,
+ 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F,
+ 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F,
+ 0xBA, 0x1F, 0xBB, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F,
+ 0xCA, 0x1F, 0xCB, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F,
+ 0xF8, 0x1F, 0xF9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F,
+ 0xFA, 0x1F, 0xFB, 0x1F, 0x7E, 0x1F, 0x7F, 0x1F,
+ 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F,
+ 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F,
+ 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F,
+ 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F,
+ 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F,
+ 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F,
+ 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F,
+ 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F,
+ 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F,
+ 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F,
+ 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F,
+ 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F,
+ 0xB8, 0x1F, 0xB9, 0x1F, 0xB2, 0x1F, 0xBC, 0x1F,
+ 0xB4, 0x1F, 0xB5, 0x1F, 0xB6, 0x1F, 0xB7, 0x1F,
+ 0xB8, 0x1F, 0xB9, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F,
+ 0xBC, 0x1F, 0xBD, 0x1F, 0xBE, 0x1F, 0xBF, 0x1F,
+ 0xC0, 0x1F, 0xC1, 0x1F, 0xC2, 0x1F, 0xC3, 0x1F,
+ 0xC4, 0x1F, 0xC5, 0x1F, 0xC6, 0x1F, 0xC7, 0x1F,
+ 0xC8, 0x1F, 0xC9, 0x1F, 0xCA, 0x1F, 0xCB, 0x1F,
+ 0xC3, 0x1F, 0xCD, 0x1F, 0xCE, 0x1F, 0xCF, 0x1F,
+ 0xD8, 0x1F, 0xD9, 0x1F, 0xD2, 0x1F, 0xD3, 0x1F,
+ 0xD4, 0x1F, 0xD5, 0x1F, 0xD6, 0x1F, 0xD7, 0x1F,
+ 0xD8, 0x1F, 0xD9, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F,
+ 0xDC, 0x1F, 0xDD, 0x1F, 0xDE, 0x1F, 0xDF, 0x1F,
+ 0xE8, 0x1F, 0xE9, 0x1F, 0xE2, 0x1F, 0xE3, 0x1F,
+ 0xE4, 0x1F, 0xEC, 0x1F, 0xE6, 0x1F, 0xE7, 0x1F,
+ 0xE8, 0x1F, 0xE9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F,
+ 0xEC, 0x1F, 0xED, 0x1F, 0xEE, 0x1F, 0xEF, 0x1F,
+ 0xF0, 0x1F, 0xF1, 0x1F, 0xF2, 0x1F, 0xF3, 0x1F,
+ 0xF4, 0x1F, 0xF5, 0x1F, 0xF6, 0x1F, 0xF7, 0x1F,
+ 0xF8, 0x1F, 0xF9, 0x1F, 0xFA, 0x1F, 0xFB, 0x1F,
+ 0xF3, 0x1F, 0xFD, 0x1F, 0xFE, 0x1F, 0xFF, 0x1F,
+ 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20,
+ 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20,
+ 0x08, 0x20, 0x09, 0x20, 0x0A, 0x20, 0x0B, 0x20,
+ 0x0C, 0x20, 0x0D, 0x20, 0x0E, 0x20, 0x0F, 0x20,
+ 0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20,
+ 0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20,
+ 0x18, 0x20, 0x19, 0x20, 0x1A, 0x20, 0x1B, 0x20,
+ 0x1C, 0x20, 0x1D, 0x20, 0x1E, 0x20, 0x1F, 0x20,
+ 0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20,
+ 0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20,
+ 0x28, 0x20, 0x29, 0x20, 0x2A, 0x20, 0x2B, 0x20,
+ 0x2C, 0x20, 0x2D, 0x20, 0x2E, 0x20, 0x2F, 0x20,
+ 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20,
+ 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20,
+ 0x38, 0x20, 0x39, 0x20, 0x3A, 0x20, 0x3B, 0x20,
+ 0x3C, 0x20, 0x3D, 0x20, 0x3E, 0x20, 0x3F, 0x20,
+ 0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20,
+ 0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20,
+ 0x48, 0x20, 0x49, 0x20, 0x4A, 0x20, 0x4B, 0x20,
+ 0x4C, 0x20, 0x4D, 0x20, 0x4E, 0x20, 0x4F, 0x20,
+ 0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20,
+ 0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20,
+ 0x58, 0x20, 0x59, 0x20, 0x5A, 0x20, 0x5B, 0x20,
+ 0x5C, 0x20, 0x5D, 0x20, 0x5E, 0x20, 0x5F, 0x20,
+ 0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20,
+ 0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20,
+ 0x68, 0x20, 0x69, 0x20, 0x6A, 0x20, 0x6B, 0x20,
+ 0x6C, 0x20, 0x6D, 0x20, 0x6E, 0x20, 0x6F, 0x20,
+ 0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20,
+ 0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20,
+ 0x78, 0x20, 0x79, 0x20, 0x7A, 0x20, 0x7B, 0x20,
+ 0x7C, 0x20, 0x7D, 0x20, 0x7E, 0x20, 0x7F, 0x20,
+ 0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20,
+ 0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20,
+ 0x88, 0x20, 0x89, 0x20, 0x8A, 0x20, 0x8B, 0x20,
+ 0x8C, 0x20, 0x8D, 0x20, 0x8E, 0x20, 0x8F, 0x20,
+ 0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20,
+ 0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20,
+ 0x98, 0x20, 0x99, 0x20, 0x9A, 0x20, 0x9B, 0x20,
+ 0x9C, 0x20, 0x9D, 0x20, 0x9E, 0x20, 0x9F, 0x20,
+ 0xA0, 0x20, 0xA1, 0x20, 0xA2, 0x20, 0xA3, 0x20,
+ 0xA4, 0x20, 0xA5, 0x20, 0xA6, 0x20, 0xA7, 0x20,
+ 0xA8, 0x20, 0xA9, 0x20, 0xAA, 0x20, 0xAB, 0x20,
+ 0xAC, 0x20, 0xAD, 0x20, 0xAE, 0x20, 0xAF, 0x20,
+ 0xB0, 0x20, 0xB1, 0x20, 0xB2, 0x20, 0xB3, 0x20,
+ 0xB4, 0x20, 0xB5, 0x20, 0xB6, 0x20, 0xB7, 0x20,
+ 0xB8, 0x20, 0xB9, 0x20, 0xBA, 0x20, 0xBB, 0x20,
+ 0xBC, 0x20, 0xBD, 0x20, 0xBE, 0x20, 0xBF, 0x20,
+ 0xC0, 0x20, 0xC1, 0x20, 0xC2, 0x20, 0xC3, 0x20,
+ 0xC4, 0x20, 0xC5, 0x20, 0xC6, 0x20, 0xC7, 0x20,
+ 0xC8, 0x20, 0xC9, 0x20, 0xCA, 0x20, 0xCB, 0x20,
+ 0xCC, 0x20, 0xCD, 0x20, 0xCE, 0x20, 0xCF, 0x20,
+ 0xD0, 0x20, 0xD1, 0x20, 0xD2, 0x20, 0xD3, 0x20,
+ 0xD4, 0x20, 0xD5, 0x20, 0xD6, 0x20, 0xD7, 0x20,
+ 0xD8, 0x20, 0xD9, 0x20, 0xDA, 0x20, 0xDB, 0x20,
+ 0xDC, 0x20, 0xDD, 0x20, 0xDE, 0x20, 0xDF, 0x20,
+ 0xE0, 0x20, 0xE1, 0x20, 0xE2, 0x20, 0xE3, 0x20,
+ 0xE4, 0x20, 0xE5, 0x20, 0xE6, 0x20, 0xE7, 0x20,
+ 0xE8, 0x20, 0xE9, 0x20, 0xEA, 0x20, 0xEB, 0x20,
+ 0xEC, 0x20, 0xED, 0x20, 0xEE, 0x20, 0xEF, 0x20,
+ 0xF0, 0x20, 0xF1, 0x20, 0xF2, 0x20, 0xF3, 0x20,
+ 0xF4, 0x20, 0xF5, 0x20, 0xF6, 0x20, 0xF7, 0x20,
+ 0xF8, 0x20, 0xF9, 0x20, 0xFA, 0x20, 0xFB, 0x20,
+ 0xFC, 0x20, 0xFD, 0x20, 0xFE, 0x20, 0xFF, 0x20,
+ 0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21,
+ 0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21,
+ 0x08, 0x21, 0x09, 0x21, 0x0A, 0x21, 0x0B, 0x21,
+ 0x0C, 0x21, 0x0D, 0x21, 0x0E, 0x21, 0x0F, 0x21,
+ 0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21,
+ 0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21,
+ 0x18, 0x21, 0x19, 0x21, 0x1A, 0x21, 0x1B, 0x21,
+ 0x1C, 0x21, 0x1D, 0x21, 0x1E, 0x21, 0x1F, 0x21,
+ 0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21,
+ 0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21,
+ 0x28, 0x21, 0x29, 0x21, 0x2A, 0x21, 0x2B, 0x21,
+ 0x2C, 0x21, 0x2D, 0x21, 0x2E, 0x21, 0x2F, 0x21,
+ 0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21,
+ 0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21,
+ 0x38, 0x21, 0x39, 0x21, 0x3A, 0x21, 0x3B, 0x21,
+ 0x3C, 0x21, 0x3D, 0x21, 0x3E, 0x21, 0x3F, 0x21,
+ 0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21,
+ 0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21,
+ 0x48, 0x21, 0x49, 0x21, 0x4A, 0x21, 0x4B, 0x21,
+ 0x4C, 0x21, 0x4D, 0x21, 0x32, 0x21, 0x4F, 0x21,
+ 0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21,
+ 0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21,
+ 0x58, 0x21, 0x59, 0x21, 0x5A, 0x21, 0x5B, 0x21,
+ 0x5C, 0x21, 0x5D, 0x21, 0x5E, 0x21, 0x5F, 0x21,
+ 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21,
+ 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21,
+ 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21,
+ 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21,
+ 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21,
+ 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21,
+ 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21,
+ 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21,
+ 0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21,
+ 0x83, 0x21, 0xFF, 0xFF, 0x4B, 0x03, 0xB6, 0x24,
+ 0xB7, 0x24, 0xB8, 0x24, 0xB9, 0x24, 0xBA, 0x24,
+ 0xBB, 0x24, 0xBC, 0x24, 0xBD, 0x24, 0xBE, 0x24,
+ 0xBF, 0x24, 0xC0, 0x24, 0xC1, 0x24, 0xC2, 0x24,
+ 0xC3, 0x24, 0xC4, 0x24, 0xC5, 0x24, 0xC6, 0x24,
+ 0xC7, 0x24, 0xC8, 0x24, 0xC9, 0x24, 0xCA, 0x24,
+ 0xCB, 0x24, 0xCC, 0x24, 0xCD, 0x24, 0xCE, 0x24,
+ 0xCF, 0x24, 0xFF, 0xFF, 0x46, 0x07, 0x00, 0x2C,
+ 0x01, 0x2C, 0x02, 0x2C, 0x03, 0x2C, 0x04, 0x2C,
+ 0x05, 0x2C, 0x06, 0x2C, 0x07, 0x2C, 0x08, 0x2C,
+ 0x09, 0x2C, 0x0A, 0x2C, 0x0B, 0x2C, 0x0C, 0x2C,
+ 0x0D, 0x2C, 0x0E, 0x2C, 0x0F, 0x2C, 0x10, 0x2C,
+ 0x11, 0x2C, 0x12, 0x2C, 0x13, 0x2C, 0x14, 0x2C,
+ 0x15, 0x2C, 0x16, 0x2C, 0x17, 0x2C, 0x18, 0x2C,
+ 0x19, 0x2C, 0x1A, 0x2C, 0x1B, 0x2C, 0x1C, 0x2C,
+ 0x1D, 0x2C, 0x1E, 0x2C, 0x1F, 0x2C, 0x20, 0x2C,
+ 0x21, 0x2C, 0x22, 0x2C, 0x23, 0x2C, 0x24, 0x2C,
+ 0x25, 0x2C, 0x26, 0x2C, 0x27, 0x2C, 0x28, 0x2C,
+ 0x29, 0x2C, 0x2A, 0x2C, 0x2B, 0x2C, 0x2C, 0x2C,
+ 0x2D, 0x2C, 0x2E, 0x2C, 0x5F, 0x2C, 0x60, 0x2C,
+ 0x60, 0x2C, 0x62, 0x2C, 0x63, 0x2C, 0x64, 0x2C,
+ 0x65, 0x2C, 0x66, 0x2C, 0x67, 0x2C, 0x67, 0x2C,
+ 0x69, 0x2C, 0x69, 0x2C, 0x6B, 0x2C, 0x6B, 0x2C,
+ 0x6D, 0x2C, 0x6E, 0x2C, 0x6F, 0x2C, 0x70, 0x2C,
+ 0x71, 0x2C, 0x72, 0x2C, 0x73, 0x2C, 0x74, 0x2C,
+ 0x75, 0x2C, 0x75, 0x2C, 0x77, 0x2C, 0x78, 0x2C,
+ 0x79, 0x2C, 0x7A, 0x2C, 0x7B, 0x2C, 0x7C, 0x2C,
+ 0x7D, 0x2C, 0x7E, 0x2C, 0x7F, 0x2C, 0x80, 0x2C,
+ 0x80, 0x2C, 0x82, 0x2C, 0x82, 0x2C, 0x84, 0x2C,
+ 0x84, 0x2C, 0x86, 0x2C, 0x86, 0x2C, 0x88, 0x2C,
+ 0x88, 0x2C, 0x8A, 0x2C, 0x8A, 0x2C, 0x8C, 0x2C,
+ 0x8C, 0x2C, 0x8E, 0x2C, 0x8E, 0x2C, 0x90, 0x2C,
+ 0x90, 0x2C, 0x92, 0x2C, 0x92, 0x2C, 0x94, 0x2C,
+ 0x94, 0x2C, 0x96, 0x2C, 0x96, 0x2C, 0x98, 0x2C,
+ 0x98, 0x2C, 0x9A, 0x2C, 0x9A, 0x2C, 0x9C, 0x2C,
+ 0x9C, 0x2C, 0x9E, 0x2C, 0x9E, 0x2C, 0xA0, 0x2C,
+ 0xA0, 0x2C, 0xA2, 0x2C, 0xA2, 0x2C, 0xA4, 0x2C,
+ 0xA4, 0x2C, 0xA6, 0x2C, 0xA6, 0x2C, 0xA8, 0x2C,
+ 0xA8, 0x2C, 0xAA, 0x2C, 0xAA, 0x2C, 0xAC, 0x2C,
+ 0xAC, 0x2C, 0xAE, 0x2C, 0xAE, 0x2C, 0xB0, 0x2C,
+ 0xB0, 0x2C, 0xB2, 0x2C, 0xB2, 0x2C, 0xB4, 0x2C,
+ 0xB4, 0x2C, 0xB6, 0x2C, 0xB6, 0x2C, 0xB8, 0x2C,
+ 0xB8, 0x2C, 0xBA, 0x2C, 0xBA, 0x2C, 0xBC, 0x2C,
+ 0xBC, 0x2C, 0xBE, 0x2C, 0xBE, 0x2C, 0xC0, 0x2C,
+ 0xC0, 0x2C, 0xC2, 0x2C, 0xC2, 0x2C, 0xC4, 0x2C,
+ 0xC4, 0x2C, 0xC6, 0x2C, 0xC6, 0x2C, 0xC8, 0x2C,
+ 0xC8, 0x2C, 0xCA, 0x2C, 0xCA, 0x2C, 0xCC, 0x2C,
+ 0xCC, 0x2C, 0xCE, 0x2C, 0xCE, 0x2C, 0xD0, 0x2C,
+ 0xD0, 0x2C, 0xD2, 0x2C, 0xD2, 0x2C, 0xD4, 0x2C,
+ 0xD4, 0x2C, 0xD6, 0x2C, 0xD6, 0x2C, 0xD8, 0x2C,
+ 0xD8, 0x2C, 0xDA, 0x2C, 0xDA, 0x2C, 0xDC, 0x2C,
+ 0xDC, 0x2C, 0xDE, 0x2C, 0xDE, 0x2C, 0xE0, 0x2C,
+ 0xE0, 0x2C, 0xE2, 0x2C, 0xE2, 0x2C, 0xE4, 0x2C,
+ 0xE5, 0x2C, 0xE6, 0x2C, 0xE7, 0x2C, 0xE8, 0x2C,
+ 0xE9, 0x2C, 0xEA, 0x2C, 0xEB, 0x2C, 0xEC, 0x2C,
+ 0xED, 0x2C, 0xEE, 0x2C, 0xEF, 0x2C, 0xF0, 0x2C,
+ 0xF1, 0x2C, 0xF2, 0x2C, 0xF3, 0x2C, 0xF4, 0x2C,
+ 0xF5, 0x2C, 0xF6, 0x2C, 0xF7, 0x2C, 0xF8, 0x2C,
+ 0xF9, 0x2C, 0xFA, 0x2C, 0xFB, 0x2C, 0xFC, 0x2C,
+ 0xFD, 0x2C, 0xFE, 0x2C, 0xFF, 0x2C, 0xA0, 0x10,
+ 0xA1, 0x10, 0xA2, 0x10, 0xA3, 0x10, 0xA4, 0x10,
+ 0xA5, 0x10, 0xA6, 0x10, 0xA7, 0x10, 0xA8, 0x10,
+ 0xA9, 0x10, 0xAA, 0x10, 0xAB, 0x10, 0xAC, 0x10,
+ 0xAD, 0x10, 0xAE, 0x10, 0xAF, 0x10, 0xB0, 0x10,
+ 0xB1, 0x10, 0xB2, 0x10, 0xB3, 0x10, 0xB4, 0x10,
+ 0xB5, 0x10, 0xB6, 0x10, 0xB7, 0x10, 0xB8, 0x10,
+ 0xB9, 0x10, 0xBA, 0x10, 0xBB, 0x10, 0xBC, 0x10,
+ 0xBD, 0x10, 0xBE, 0x10, 0xBF, 0x10, 0xC0, 0x10,
+ 0xC1, 0x10, 0xC2, 0x10, 0xC3, 0x10, 0xC4, 0x10,
+ 0xC5, 0x10, 0xFF, 0xFF, 0x1B, 0xD2, 0x21, 0xFF,
+ 0x22, 0xFF, 0x23, 0xFF, 0x24, 0xFF, 0x25, 0xFF,
+ 0x26, 0xFF, 0x27, 0xFF, 0x28, 0xFF, 0x29, 0xFF,
+ 0x2A, 0xFF, 0x2B, 0xFF, 0x2C, 0xFF, 0x2D, 0xFF,
+ 0x2E, 0xFF, 0x2F, 0xFF, 0x30, 0xFF, 0x31, 0xFF,
+ 0x32, 0xFF, 0x33, 0xFF, 0x34, 0xFF, 0x35, 0xFF,
+ 0x36, 0xFF, 0x37, 0xFF, 0x38, 0xFF, 0x39, 0xFF,
+ 0x3A, 0xFF, 0x5B, 0xFF, 0x5C, 0xFF, 0x5D, 0xFF,
+ 0x5E, 0xFF, 0x5F, 0xFF, 0x60, 0xFF, 0x61, 0xFF,
+ 0x62, 0xFF, 0x63, 0xFF, 0x64, 0xFF, 0x65, 0xFF,
+ 0x66, 0xFF, 0x67, 0xFF, 0x68, 0xFF, 0x69, 0xFF,
+ 0x6A, 0xFF, 0x6B, 0xFF, 0x6C, 0xFF, 0x6D, 0xFF,
+ 0x6E, 0xFF, 0x6F, 0xFF, 0x70, 0xFF, 0x71, 0xFF,
+ 0x72, 0xFF, 0x73, 0xFF, 0x74, 0xFF, 0x75, 0xFF,
+ 0x76, 0xFF, 0x77, 0xFF, 0x78, 0xFF, 0x79, 0xFF,
+ 0x7A, 0xFF, 0x7B, 0xFF, 0x7C, 0xFF, 0x7D, 0xFF,
+ 0x7E, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0x81, 0xFF,
+ 0x82, 0xFF, 0x83, 0xFF, 0x84, 0xFF, 0x85, 0xFF,
+ 0x86, 0xFF, 0x87, 0xFF, 0x88, 0xFF, 0x89, 0xFF,
+ 0x8A, 0xFF, 0x8B, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF,
+ 0x8E, 0xFF, 0x8F, 0xFF, 0x90, 0xFF, 0x91, 0xFF,
+ 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, 0xFF,
+ 0x96, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF,
+ 0x9A, 0xFF, 0x9B, 0xFF, 0x9C, 0xFF, 0x9D, 0xFF,
+ 0x9E, 0xFF, 0x9F, 0xFF, 0xA0, 0xFF, 0xA1, 0xFF,
+ 0xA2, 0xFF, 0xA3, 0xFF, 0xA4, 0xFF, 0xA5, 0xFF,
+ 0xA6, 0xFF, 0xA7, 0xFF, 0xA8, 0xFF, 0xA9, 0xFF,
+ 0xAA, 0xFF, 0xAB, 0xFF, 0xAC, 0xFF, 0xAD, 0xFF,
+ 0xAE, 0xFF, 0xAF, 0xFF, 0xB0, 0xFF, 0xB1, 0xFF,
+ 0xB2, 0xFF, 0xB3, 0xFF, 0xB4, 0xFF, 0xB5, 0xFF,
+ 0xB6, 0xFF, 0xB7, 0xFF, 0xB8, 0xFF, 0xB9, 0xFF,
+ 0xBA, 0xFF, 0xBB, 0xFF, 0xBC, 0xFF, 0xBD, 0xFF,
+ 0xBE, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF,
+ 0xC2, 0xFF, 0xC3, 0xFF, 0xC4, 0xFF, 0xC5, 0xFF,
+ 0xC6, 0xFF, 0xC7, 0xFF, 0xC8, 0xFF, 0xC9, 0xFF,
+ 0xCA, 0xFF, 0xCB, 0xFF, 0xCC, 0xFF, 0xCD, 0xFF,
+ 0xCE, 0xFF, 0xCF, 0xFF, 0xD0, 0xFF, 0xD1, 0xFF,
+ 0xD2, 0xFF, 0xD3, 0xFF, 0xD4, 0xFF, 0xD5, 0xFF,
+ 0xD6, 0xFF, 0xD7, 0xFF, 0xD8, 0xFF, 0xD9, 0xFF,
+ 0xDA, 0xFF, 0xDB, 0xFF, 0xDC, 0xFF, 0xDD, 0xFF,
+ 0xDE, 0xFF, 0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF,
+ 0xE2, 0xFF, 0xE3, 0xFF, 0xE4, 0xFF, 0xE5, 0xFF,
+ 0xE6, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF,
+ 0xEA, 0xFF, 0xEB, 0xFF, 0xEC, 0xFF, 0xED, 0xFF,
+ 0xEE, 0xFF, 0xEF, 0xFF, 0xF0, 0xFF, 0xF1, 0xFF,
+ 0xF2, 0xFF, 0xF3, 0xFF, 0xF4, 0xFF, 0xF5, 0xFF,
+ 0xF6, 0xFF, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xFF,
+ 0xFA, 0xFF, 0xFB, 0xFF, 0xFC, 0xFF, 0xFD, 0xFF,
+ 0xFE, 0xFF, 0xFF, 0xFF
+};
--
2.23.0
On Wed, Aug 28, 2019 at 06:08:17PM +0200, Greg Kroah-Hartman wrote:
> From: Valdis Klētnieks <[email protected]>
>
> The exfat code needs a lot of work to get it into "real" shape for
> the fs/ part of the kernel, so put it into drivers/staging/ for now so
> that it can be worked on by everyone in the community.
>
> The full specification of the filesystem can be found at:
> https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification
>
> Signed-off-by: Valdis Kletnieks <[email protected]>
> Signed-off-by: Sasha Levin <[email protected]>
> Signed-off-by: Greg Kroah-Hartman <[email protected]>
> ---
> MAINTAINERS | 5 +
> drivers/staging/Kconfig | 2 +
> drivers/staging/Makefile | 1 +
> drivers/staging/exfat/Kconfig | 39 +
> drivers/staging/exfat/Makefile | 10 +
> drivers/staging/exfat/TODO | 12 +
> drivers/staging/exfat/exfat.h | 973 ++++++
> drivers/staging/exfat/exfat_blkdev.c | 136 +
> drivers/staging/exfat/exfat_cache.c | 722 +++++
> drivers/staging/exfat/exfat_core.c | 3704 +++++++++++++++++++++++
> drivers/staging/exfat/exfat_nls.c | 404 +++
> drivers/staging/exfat/exfat_super.c | 4137 ++++++++++++++++++++++++++
> drivers/staging/exfat/exfat_upcase.c | 740 +++++
> 13 files changed, 10885 insertions(+)
> create mode 100644 drivers/staging/exfat/Kconfig
> create mode 100644 drivers/staging/exfat/Makefile
> create mode 100644 drivers/staging/exfat/TODO
> create mode 100644 drivers/staging/exfat/exfat.h
> create mode 100644 drivers/staging/exfat/exfat_blkdev.c
> create mode 100644 drivers/staging/exfat/exfat_cache.c
> create mode 100644 drivers/staging/exfat/exfat_core.c
> create mode 100644 drivers/staging/exfat/exfat_nls.c
> create mode 100644 drivers/staging/exfat/exfat_super.c
> create mode 100644 drivers/staging/exfat/exfat_upcase.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index e3242687cd19..a484b36e5117 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6097,6 +6097,11 @@ F: include/trace/events/mdio.h
> F: include/uapi/linux/mdio.h
> F: include/uapi/linux/mii.h
>
> +EXFAT FILE SYSTEM
> +M: Valdis Kletnieks <[email protected]>
> +S: Maintained
> +F: fs/exfat/
Oops, I messed this line up. I moved this to drivers/staging/ and I
forgot to update this line. I'll do it in a follow-on patch.
thanks,
greg k-h
Can we please just review the damn thing and get it into the proper
tree? That whole concept of staging file systems just has been one
fricking disaster, including Greg just moving not fully reviewed ones
over like erofs just because he feels like it. I'm getting sick and
tired of this scheme.
On Wed, Aug 28, 2019 at 11:23:40PM -0700, Christoph Hellwig wrote:
> Can we please just review the damn thing and get it into the proper
> tree? That whole concept of staging file systems just has been one
> fricking disaster, including Greg just moving not fully reviewed ones
> over like erofs just because he feels like it. I'm getting sick and
> tired of this scheme.
For this filesystem, it's going to be a _lot_ of work before that can
happen, and I'd really like to have lots of people help out with it
instead of it living in random github trees for long periods of time.
Putting it in the kernel lets all of the people that have been spending
time on this work together in one place.
Given that the vfs apis change so infrequently, I don't really
understand the objection here. If you change apis in the rest of the
kernel, don't worry about anything in drivers/staging/ I will fix that
up. This code has a much smaller api-interaction-level than lots of the
drivers that are currently living in staging.
thanks,
greg k-h
Hi Christoph,
On Wed, Aug 28, 2019 at 11:23:40PM -0700, Christoph Hellwig wrote:
> Can we please just review the damn thing and get it into the proper
> tree? That whole concept of staging file systems just has been one
> fricking disaster, including Greg just moving not fully reviewed ones
> over like erofs just because he feels like it. I'm getting sick and
> tired of this scheme.
I just want to a word on EROFS stuff (I'm not suitable to comment
on the current exfat implementation). Since EROFS stuff has been
in staging tree for more than a year, anyone who wants to review
it can review this filesystem at any time.
EROFS is not just a homebrew or experimental fs for now, it has been
widely used for many commerical smartphones, and we will upstream it
to AOSP for more Android smartphones after it gets merged to upstream.
I personally cc-ed you for a month, and I didn't get any objection
from others (including Linus) for about 2 months. That isn't because
of someone likes it, rather we cannot make no progress compared with
some exist fs community because this is our love work.
And it's self-contained driver at least, and it's disabled by default,
It cannot be stayed in staging tree to do a lot of EROFS feature
improvements itself forever (since it is cleaned enough).
It has proven its validity as well.
Thanks,
Gao Xiang
Hi Christoph,
On Thu, Aug 29, 2019 at 03:01:49PM +0800, Gao Xiang wrote:
> Hi Christoph,
>
> On Wed, Aug 28, 2019 at 11:23:40PM -0700, Christoph Hellwig wrote:
> > Can we please just review the damn thing and get it into the proper
> > tree? That whole concept of staging file systems just has been one
> > fricking disaster, including Greg just moving not fully reviewed ones
> > over like erofs just because he feels like it. I'm getting sick and
> > tired of this scheme.
>
> I just want to a word on EROFS stuff (I'm not suitable to comment
> on the current exfat implementation). Since EROFS stuff has been
> in staging tree for more than a year, anyone who wants to review
> it can review this filesystem at any time.
>
> EROFS is not just a homebrew or experimental fs for now, it has been
> widely used for many commerical smartphones, and we will upstream it
> to AOSP for more Android smartphones after it gets merged to upstream.
> I personally cc-ed you for a month, and I didn't get any objection
> from others (including Linus) for about 2 months. That isn't because
> of someone likes it, rather we cannot make no progress compared with
> some exist fs community because this is our love work.
>
> And it's self-contained driver at least, and it's disabled by default,
> It cannot be stayed in staging tree to do a lot of EROFS feature
> improvements itself forever (since it is cleaned enough).
> It has proven its validity as well.
It seems I misunderstood your idea, sorry about that... EROFS
properly uses vfs interfaces (e.g. we also considered RCU symlink
lookup path at the very beginning of our design as Al said [1],
except for mount interface as Al mentioned [2] (thanks him for
taking some time on it), it was used for our debugging use),
and it didn't cause any extra burden to vfs or other subsystems.
[1] https://lore.kernel.org/r/[email protected]/
[2] https://lore.kernel.org/r/[email protected]/
Thanks,
Gao Xiang
>
> Thanks,
> Gao Xiang
>
On Thu, Aug 29, 2019 at 08:39:55AM +0200, Greg Kroah-Hartman wrote:
> On Wed, Aug 28, 2019 at 11:23:40PM -0700, Christoph Hellwig wrote:
> > Can we please just review the damn thing and get it into the proper
> > tree? That whole concept of staging file systems just has been one
> > fricking disaster, including Greg just moving not fully reviewed ones
> > over like erofs just because he feels like it. I'm getting sick and
> > tired of this scheme.
>
> For this filesystem, it's going to be a _lot_ of work before that can
> happen, and I'd really like to have lots of people help out with it
> instead of it living in random github trees for long periods of time.
Did you actually look at the thing instead of blindly applying some
pile of crap?
It basically is a reimplementation of fs/fat/ not up to kernel standards
with a few indirections thrown in to also support exfat. So no amount
of work on this codebase is really going to bring us forward. Instead
someone how can spend a couple days on this and actually has file
systems to test it just needs to bring the low-level format bits over
to our well tested fs/fat codebase instead of duplicating it.
On Thu, Aug 29, 2019 at 02:41:36AM -0700, Christoph Hellwig wrote:
> On Thu, Aug 29, 2019 at 08:39:55AM +0200, Greg Kroah-Hartman wrote:
> > On Wed, Aug 28, 2019 at 11:23:40PM -0700, Christoph Hellwig wrote:
> > > Can we please just review the damn thing and get it into the proper
> > > tree? That whole concept of staging file systems just has been one
> > > fricking disaster, including Greg just moving not fully reviewed ones
> > > over like erofs just because he feels like it. I'm getting sick and
> > > tired of this scheme.
> >
> > For this filesystem, it's going to be a _lot_ of work before that can
> > happen, and I'd really like to have lots of people help out with it
> > instead of it living in random github trees for long periods of time.
>
> Did you actually look at the thing instead of blindly applying some
> pile of crap?
>
> It basically is a reimplementation of fs/fat/ not up to kernel standards
> with a few indirections thrown in to also support exfat. So no amount
> of work on this codebase is really going to bring us forward. Instead
> someone how can spend a couple days on this and actually has file
> systems to test it just needs to bring the low-level format bits over
> to our well tested fs/fat codebase instead of duplicating it.
I did try just that, a few years ago, and gave up on it. I don't think
it can be added to the existing vfat code base but I am willing to be
proven wrong.
Now that we have the specs, it might be easier, and the vfat spec is a
subset of the exfat spec, but to get stuff working today, for users,
it's good to have it in staging. We can do the normal, "keep it in
stable, get a clean-room implementation merged like usual, and then
delete the staging version" three step process like we have done a
number of times already as well.
I know the code is horrible, but I will gladly take horrible code into
staging. If it bothers you, just please ignore it. That's what staging
is there for :)
thanks,
greg k-h
On Thu, Aug 29, 2019 at 04:24:09PM +0800, Gao Xiang wrote:
> It seems I misunderstood your idea, sorry about that... EROFS
> properly uses vfs interfaces (e.g. we also considered RCU symlink
> lookup path at the very beginning of our design as Al said [1],
> except for mount interface as Al mentioned [2] (thanks him for
> taking some time on it), it was used for our debugging use),
> and it didn't cause any extra burden to vfs or other subsystems.
It would still have been a lot less effort for everyone without
the idiotic staging detour.
On Thu, Aug 29, 2019 at 11:50:19AM +0200, Greg Kroah-Hartman wrote:
> I did try just that, a few years ago, and gave up on it. I don't think
> it can be added to the existing vfat code base but I am willing to be
> proven wrong.
And what exactly was the problem?
>
> Now that we have the specs, it might be easier, and the vfat spec is a
> subset of the exfat spec, but to get stuff working today, for users,
> it's good to have it in staging. We can do the normal, "keep it in
> stable, get a clean-room implementation merged like usual, and then
> delete the staging version" three step process like we have done a
> number of times already as well.
>
> I know the code is horrible, but I will gladly take horrible code into
> staging. If it bothers you, just please ignore it. That's what staging
> is there for :)
And then after a while you decide it's been long enough and force move
it out of staging like the POS erofs code?
On Thu, Aug 29, 2019 at 03:37:49AM -0700, Christoph Hellwig wrote:
> On Thu, Aug 29, 2019 at 11:50:19AM +0200, Greg Kroah-Hartman wrote:
> > I did try just that, a few years ago, and gave up on it. I don't think
> > it can be added to the existing vfat code base but I am willing to be
> > proven wrong.
>
> And what exactly was the problem?
>
> >
> > Now that we have the specs, it might be easier, and the vfat spec is a
> > subset of the exfat spec, but to get stuff working today, for users,
> > it's good to have it in staging. We can do the normal, "keep it in
> > stable, get a clean-room implementation merged like usual, and then
> > delete the staging version" three step process like we have done a
> > number of times already as well.
> >
> > I know the code is horrible, but I will gladly take horrible code into
> > staging. If it bothers you, just please ignore it. That's what staging
> > is there for :)
>
> And then after a while you decide it's been long enough and force move
> it out of staging like the POS erofs code?
The problem is that EROFS has been there for a year and
I sent v1-v8 patches here, You didn't review or reply it
once until now.
And I have no idea what is the relationship between EROFS
and the current exfat implementation.
Thanks,
Gao Xiang
On Thu, Aug 29, 2019 at 03:37:49AM -0700, Christoph Hellwig wrote:
> On Thu, Aug 29, 2019 at 11:50:19AM +0200, Greg Kroah-Hartman wrote:
> > I did try just that, a few years ago, and gave up on it. I don't think
> > it can be added to the existing vfat code base but I am willing to be
> > proven wrong.
>
> And what exactly was the problem?
At the time, I just couldn't figure out how to do it as I had no spec,
and only a bad code-base to go off of. I'm sure someone else might be
able to do to it :)
> > Now that we have the specs, it might be easier, and the vfat spec is a
> > subset of the exfat spec, but to get stuff working today, for users,
> > it's good to have it in staging. We can do the normal, "keep it in
> > stable, get a clean-room implementation merged like usual, and then
> > delete the staging version" three step process like we have done a
> > number of times already as well.
> >
> > I know the code is horrible, but I will gladly take horrible code into
> > staging. If it bothers you, just please ignore it. That's what staging
> > is there for :)
>
> And then after a while you decide it's been long enough and force move
> it out of staging like the POS erofs code?
Hey, that's not nice, erofs isn't a POS. It could always use more
review, which the developers asked for numerous times.
There's nothing different from a filesystem compared to a driver. If
its stand-alone, and touches nothing else, all issues with it are
self-contained and do not bother anyone else in the kernel. We merge
drivers all the time that need more work because our review cycles are
low. And review cycles for vfs developers are even more scarce than
driver reviewers.
thanks,
greg k-h
On Thu, Aug 29, 2019 at 07:04:43PM +0800, Gao Xiang wrote:
> On Thu, Aug 29, 2019 at 03:37:49AM -0700, Christoph Hellwig wrote:
> > On Thu, Aug 29, 2019 at 11:50:19AM +0200, Greg Kroah-Hartman wrote:
> > > I did try just that, a few years ago, and gave up on it. I don't think
> > > it can be added to the existing vfat code base but I am willing to be
> > > proven wrong.
> >
> > And what exactly was the problem?
> >
> > >
> > > Now that we have the specs, it might be easier, and the vfat spec is a
> > > subset of the exfat spec, but to get stuff working today, for users,
> > > it's good to have it in staging. We can do the normal, "keep it in
> > > stable, get a clean-room implementation merged like usual, and then
> > > delete the staging version" three step process like we have done a
> > > number of times already as well.
> > >
> > > I know the code is horrible, but I will gladly take horrible code into
> > > staging. If it bothers you, just please ignore it. That's what staging
> > > is there for :)
> >
> > And then after a while you decide it's been long enough and force move
> > it out of staging like the POS erofs code?
>
> The problem is that EROFS has been there for a year and
> I sent v1-v8 patches here, You didn't review or reply it
> once until now.
>
> And I have no idea what is the relationship between EROFS
> and the current exfat implementation.
There isn't any, other than it too is a filesystem that was in the
staging directory :)
greg k-h
On Wednesday 28 August 2019 18:08:17 Greg Kroah-Hartman wrote:
> The full specification of the filesystem can be found at:
> https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification
This is not truth. This specification is not "full". There are missing
important details, like how is TexFAT implemented. In that specification
is just reference to other unpublished documents. So it is classic MS
way, they release something incomplete and incompatible with their own
NT implementation, like with FAT32 vs fastfat.sys.
I would be very very careful about existence of such documentation until
somebody implement it and do some testing against MS own implementation.
--
Pali Rohár
[email protected]
On Thu, 29 Aug 2019 14:14:35 +0200, Pali Roh?r said:
> On Wednesday 28 August 2019 18:08:17 Greg Kroah-Hartman wrote:
> > The full specification of the filesystem can be found at:
> > https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification
>
> This is not truth. This specification is not "full". There are missing
> important details, like how is TexFAT implemented.
Well..given that the spec says it's an extension used by Windows CE...
> 1.5 Windows CE and TexFAT
> TexFAT is an extension to exFAT that adds transaction-safe operational
> semantics on top of the base file system. TexFAT is used by Windows CE. TexFAT
> requires the use of the two FATs and allocation bitmaps for use in
> transactions. It also defines several additional structures including padding
> descriptors and security descriptors.
And these two pieces of info:
> 3.1.13.1 ActiveFat Field
> The ActiveFat field shall describe which FAT and Allocation Bitmap are active
> (and implementations shall use), as follows:
> 0, which means the First FAT and First Allocation Bitmap are active
> 1, which means the Second FAT and Second Allocation Bitmap are active and is
> possible only when the NumberOfFats field contains the value 2
> Implementations shall consider the inactive FAT and Allocation Bitmap as stale.
> Only TexFAT-aware implementations shall switch the active FAT and Allocation
> Bitmaps (see Section 7.1).
> 3.1.16 NumberOfFats Field
> The NumberOfFats field shall describe the number of FATs and Allocation Bitmaps
> the volume contains.
> The valid range of values for this field shall be:
> 1, which indicates the volume only contains the First FAT and First Allocation Bitmap
> 2, which indicates the volume contains the First FAT, Second FAT, First
> Allocation Bitmap, and Second Allocation Bitmap; this value is only valid for
> TexFAT volumes
I think we're OK if we just set ActiveFat to 0 and NumberOfFats to 1.
Unless somebody has actual evidence of a non-WindowsCE extfat that has
NumberOfFats == 2....
On Thursday 29 August 2019 08:34:04 Valdis Klētnieks wrote:
> On Thu, 29 Aug 2019 14:14:35 +0200, Pali Roh?r said:
> > On Wednesday 28 August 2019 18:08:17 Greg Kroah-Hartman wrote:
> > > The full specification of the filesystem can be found at:
> > > https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification
> >
> > This is not truth. This specification is not "full". There are missing
> > important details, like how is TexFAT implemented.
>
> Well..given that the spec says it's an extension used by Windows CE...
It is extension which provides more error-prone write operations to
minimize damage on filesystem.
> > 1.5 Windows CE and TexFAT
>
> > TexFAT is an extension to exFAT that adds transaction-safe operational
> > semantics on top of the base file system. TexFAT is used by Windows CE. TexFAT
> > requires the use of the two FATs and allocation bitmaps for use in
> > transactions. It also defines several additional structures including padding
> > descriptors and security descriptors.
>
> And these two pieces of info:
>
> > 3.1.13.1 ActiveFat Field
>
> > The ActiveFat field shall describe which FAT and Allocation Bitmap are active
> > (and implementations shall use), as follows:
>
> > 0, which means the First FAT and First Allocation Bitmap are active
>
> > 1, which means the Second FAT and Second Allocation Bitmap are active and is
> > possible only when the NumberOfFats field contains the value 2
>
> > Implementations shall consider the inactive FAT and Allocation Bitmap as stale.
> > Only TexFAT-aware implementations shall switch the active FAT and Allocation
> > Bitmaps (see Section 7.1).
>
> > 3.1.16 NumberOfFats Field
> > The NumberOfFats field shall describe the number of FATs and Allocation Bitmaps
> > the volume contains.
>
> > The valid range of values for this field shall be:
>
> > 1, which indicates the volume only contains the First FAT and First Allocation Bitmap
>
> > 2, which indicates the volume contains the First FAT, Second FAT, First
> > Allocation Bitmap, and Second Allocation Bitmap; this value is only valid for
> > TexFAT volumes
>
> I think we're OK if we just set ActiveFat to 0 and NumberOfFats to 1.
But this degrades whole FS. Even FAT32 uses two FAT tables due to big
factor of brokenness and fsck takes care of it when repairing.
There is not too much sense to use exFAT with just one FAT if we have
already working FAT32 with redundancy of FAT table.
> Unless somebody has actual evidence of a non-WindowsCE extfat that has
> NumberOfFats == 2....
--
Pali Rohár
[email protected]
> +++ b/drivers/staging/exfat/exfat.h
> @@ -0,0 +1,973 @@
…
> +/* file types */
> +#define TYPE_UNUSED 0x0000
> +#define TYPE_DELETED 0x0001
…
> +/* time modes */
> +#define TM_CREATE 0
> +#define TM_MODIFY 1
Will it be helpful to work with enumerations at such places?
Regards,
Markus
On Thu, Aug 29, 2019 at 01:18:10PM +0200, Greg Kroah-Hartman wrote:
> It could always use more review, which the developers asked for
> numerous times.
I stopped commenting on erofs style because all the likely/unlikely()
nonsense is a pet peeve.
regards,
dan carpenter
Hi Dan,
On Thu, Aug 29, 2019 at 06:11:44PM +0300, Dan Carpenter wrote:
> On Thu, Aug 29, 2019 at 01:18:10PM +0200, Greg Kroah-Hartman wrote:
> > It could always use more review, which the developers asked for
> > numerous times.
>
> I stopped commenting on erofs style because all the likely/unlikely()
> nonsense is a pet peeve.
I don't know what is wrong for EROFS to use unlikely for all error
handling path (that is what IS_ERR implys).
If you like, I will delete them all.
p.s. There are 2947 (un)likely places in fs/ directory.
Thanks,
Gao Xiang
>
> regards,
> dan carpenter
>
> +++ b/drivers/staging/exfat/exfat_core.c
> @@ -0,0 +1,3704 @@
…
> +static s32 __load_upcase_table(struct super_block *sb, sector_t sector,
> + u32 num_sectors, u32 utbl_checksum)
> +{
…
> +error:
An other label would be nicer, wouldn't it?
> + if (tmp_bh)
> + brelse(tmp_bh);
This inline function tolerates the passing of null pointers.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/buffer_head.h?id=9cf6b756cdf2cd38b8b0dac2567f7c6daf5e79d5#n292
https://elixir.bootlin.com/linux/v5.3-rc6/source/include/linux/buffer_head.h#L292
Thus I suggest to omit the extra pointer check at affected places.
> +++ b/drivers/staging/exfat/exfat_super.c
> @@ -0,0 +1,4137 @@
…
> +// FIXME use commented lines
> +// static int exfat_default_codepage = CONFIG_EXFAT_DEFAULT_CODEPAGE;
Is such information still relevant anyhow?
> +static int exfat_fill_super(struct super_block *sb, void *data, int silent)
> +{
…
> +out_fail:
> + if (root_inode)
> + iput(root_inode);
I am informed in the way that this function tolerates the passing
of null pointers.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/inode.c?id=9cf6b756cdf2cd38b8b0dac2567f7c6daf5e79d5#n1564
https://elixir.bootlin.com/linux/v5.3-rc6/source/fs/inode.c#L1564
Thus I suggest to omit the extra pointer check also at this place.
Regards,
Markus
> p.s. There are 2947 (un)likely places in fs/ directory.
I was complaining about you adding new pointless ones, not existing
ones. The likely/unlikely annotations are supposed to be functional and
not decorative. I explained this very clearly.
Probably most of the annotations in fs/ are wrong but they are also
harmless except for the slight messiness. However there are definitely
some which are important so removing them all isn't a good idea.
> If you like, I will delete them all.
But for erofs, I don't think that any of the likely/unlikely calls have
been thought about so I'm fine with removing all of them in one go.
regards,
dan carpenter
Hi Dan,
On Thu, Aug 29, 2019 at 06:43:46PM +0300, Dan Carpenter wrote:
> > p.s. There are 2947 (un)likely places in fs/ directory.
>
> I was complaining about you adding new pointless ones, not existing
> ones. The likely/unlikely annotations are supposed to be functional and
> not decorative. I explained this very clearly.
I don't think that is mostly pointless. I think it has functional use
because all error handling paths are rarely happened, or can you remove
the unlikely in IS_ERR as well?
>
> Probably most of the annotations in fs/ are wrong but they are also
> harmless except for the slight messiness. However there are definitely
> some which are important so removing them all isn't a good idea.
>
> > If you like, I will delete them all.
>
> But for erofs, I don't think that any of the likely/unlikely calls have
> been thought about so I'm fine with removing all of them in one go.
Maybe some misuse but rare, I will show you case by case. Wait a minute.
Thanks,
Gao Xiang
>
> regards,
> dan carpenter
>
Hi Dan,
On Thu, Aug 29, 2019 at 11:51:27PM +0800, Gao Xiang wrote:
> Hi Dan,
>
> On Thu, Aug 29, 2019 at 06:43:46PM +0300, Dan Carpenter wrote:
> > > p.s. There are 2947 (un)likely places in fs/ directory.
> >
> > I was complaining about you adding new pointless ones, not existing
> > ones. The likely/unlikely annotations are supposed to be functional and
> > not decorative. I explained this very clearly.
>
> I don't think that is mostly pointless. I think it has functional use
> because all error handling paths are rarely happened, or can you remove
> the unlikely in IS_ERR as well?
>
> >
> > Probably most of the annotations in fs/ are wrong but they are also
> > harmless except for the slight messiness. However there are definitely
> > some which are important so removing them all isn't a good idea.
> >
> > > If you like, I will delete them all.
> >
> > But for erofs, I don't think that any of the likely/unlikely calls have
> > been thought about so I'm fine with removing all of them in one go.
>
> Maybe some misuse but rare, I will show you case by case. Wait a minute.
Anyway, I'm fine to delete them all if you like, but I think majority of these
are meaningful.
data.c- /* page is already locked */
data.c- DBG_BUGON(PageUptodate(page));
data.c-
data.c: if (unlikely(err))
data.c- SetPageError(page);
data.c- else
data.c- SetPageUptodate(page);
--
IO error
data.c-
data.c-repeat:
data.c- page = find_or_create_page(mapping, blkaddr, gfp);
data.c: if (unlikely(!page)) {
data.c- DBG_BUGON(nofail);
data.c- return ERR_PTR(-ENOMEM);
data.c- }
--
NO MEM
data.c- }
data.c-
data.c- err = bio_add_page(bio, page, PAGE_SIZE, 0);
data.c: if (unlikely(err != PAGE_SIZE)) {
data.c- err = -EFAULT;
data.c- goto err_out;
Internal error (since it is a single bio with one page).
data.c- }
---
data.c- lock_page(page);
data.c-
data.c- /* this page has been truncated by others */
data.c: if (unlikely(page->mapping != mapping)) {
data.c-unlock_repeat:
data.c- unlock_page(page);
data.c- put_page(page);
data.c- goto repeat;
data.c- }
truncated
data.c-
data.c: /* more likely a read error */
data.c: if (unlikely(!PageUptodate(page))) {
data.c- if (io_retries) {
data.c- --io_retries;
data.c- goto unlock_repeat;
IO err
--
data.c- nblocks = DIV_ROUND_UP(inode->i_size, PAGE_SIZE);
data.c- lastblk = nblocks - is_inode_flat_inline(inode);
data.c-
data.c: if (unlikely(offset >= inode->i_size)) {
data.c- /* leave out-of-bound access unmapped */
data.c- map->m_flags = 0;
data.c- map->m_plen = 0;
--
FS corrupted
data.c-int erofs_map_blocks(struct inode *inode,
data.c- struct erofs_map_blocks *map, int flags)
data.c-{
data.c: if (unlikely(is_inode_layout_compression(inode))) {
for compressed files, should call z_erofs_map_blocks_iter since it
behaves in iterative way. but I think that can be deleted.
data.c- int err = z_erofs_map_blocks_iter(inode, map, flags);
data.c-
data.c- if (map->mpage) {
--
data.c- unsigned int blkoff;
data.c-
data.c- err = erofs_map_blocks(inode, &map, EROFS_GET_BLOCKS_RAW);
data.c: if (unlikely(err))
data.c- goto err_out;
Error
data.c-
data.c- /* zero out the holed page */
data.c: if (unlikely(!(map.m_flags & EROFS_MAP_MAPPED))) {
no hole in erofs.
data.c- zero_user_segment(page, 0, PAGE_SIZE);
data.c- SetPageUptodate(page);
data.c-
--
data.c-submit_bio_out:
data.c- __submit_bio(bio, REQ_OP_READ, 0);
data.c-
data.c: return unlikely(err) ? ERR_PTR(err) : NULL;
err
data.c-}
data.c-
data.c-/*
--
data.c- DBG_BUGON(!list_empty(pages));
data.c-
data.c- /* the rare case (end in gaps) */
data.c: if (unlikely(bio))
data.c- __submit_bio(bio, REQ_OP_READ, 0);
data.c- return 0;
data.c-}
decompressor.c- get_page(victim);
decompressor.c- } else {
decompressor.c- victim = erofs_allocpage(pagepool, GFP_KERNEL, false);
decompressor.c: if (unlikely(!victim))
decompressor.c- return -ENOMEM;
nomem
decompressor.c- victim->mapping = Z_EROFS_MAPPING_STAGING;
decompressor.c- }
dir.c- de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
dir.c-
dir.c- /* a corrupted entry is found */
dir.c: if (unlikely(nameoff + de_namelen > maxsize ||
dir.c- de_namelen > EROFS_NAME_LEN)) {
corrupted
dir.c- errln("bogus dirent @ nid %llu", EROFS_V(dir)->nid);
dir.c- DBG_BUGON(1);
--
dir.c-
dir.c- nameoff = le16_to_cpu(de->nameoff);
dir.c-
dir.c: if (unlikely(nameoff < sizeof(struct erofs_dirent) ||
dir.c- nameoff >= PAGE_SIZE)) {
corrupted
dir.c- errln("%s, invalid de[0].nameoff %u @ nid %llu",
dir.c- __func__, nameoff, EROFS_V(dir)->nid);
--
dir.c- dirsize - ctx->pos + ofs, PAGE_SIZE);
dir.c-
dir.c- /* search dirents at the arbitrary position */
dir.c: if (unlikely(initial)) {
as comments said
dir.c- initial = false;
dir.c-
dir.c- ofs = roundup(ofs, sizeof(struct erofs_dirent));
dir.c: if (unlikely(ofs >= nameoff))
dir.c- goto skip_this;
dir.c- }
dir.c-
--
dir.c-
dir.c- ctx->pos = blknr_to_addr(i) + ofs;
dir.c-
dir.c: if (unlikely(err))
err
dir.c- break;
dir.c- ++i;
dir.c- ofs = 0;
inode.c-
inode.c- vi->datamode = __inode_data_mapping(advise);
inode.c-
inode.c: if (unlikely(vi->datamode >= EROFS_INODE_LAYOUT_MAX)) {
inode.c- errln("unsupported data mapping %u of nid %llu",
inode.c- vi->datamode, vi->nid);
inode.c- DBG_BUGON(1);
err
--
inode.c- if (S_ISLNK(inode->i_mode) && inode->i_size < PAGE_SIZE) {
inode.c- char *lnk = erofs_kmalloc(sbi, inode->i_size + 1, GFP_KERNEL);
inode.c-
inode.c: if (unlikely(!lnk))
inode.c- return -ENOMEM;
inode.c-
inode.c- m_pofs += vi->inode_isize + vi->xattr_isize;
inode.c-
inode.c- /* inline symlink data shouldn't across page boundary as well */
inode.c: if (unlikely(m_pofs + inode->i_size > PAGE_SIZE)) {
inode.c- kfree(lnk);
inode.c- errln("inline data cross block boundary @ nid %llu",
inode.c- vi->nid);
err
--
inode.c-{
inode.c- struct inode *inode = erofs_iget_locked(sb, nid);
inode.c-
inode.c: if (unlikely(!inode))
inode.c- return ERR_PTR(-ENOMEM);
err
inode.c-
inode.c- if (inode->i_state & I_NEW) {
--
inode.c- vi->nid = nid;
inode.c-
inode.c- err = fill_inode(inode, isdir);
inode.c: if (likely(!err))
inode.c- unlock_new_inode(inode);
inode.c- else {
err
inode.c- iget_failed(inode);
I will stop here.
Thanks,
Gao Xiang
>
> Thanks,
> Gao Xiang
>
> >
> > regards,
> > dan carpenter
> >
Hi Dan,
On Thu, Aug 29, 2019 at 11:43:46PM +0800, Dan Carpenter wrote:
> > p.s. There are 2947 (un)likely places in fs/ directory.
>
> I was complaining about you adding new pointless ones, not existing
> ones. The likely/unlikely annotations are supposed to be functional and
> not decorative. I explained this very clearly.
>
> Probably most of the annotations in fs/ are wrong but they are also
> harmless except for the slight messiness. However there are definitely
> some which are important so removing them all isn't a good idea.
>
> > If you like, I will delete them all.
>
> But for erofs, I don't think that any of the likely/unlikely calls have
> been thought about so I'm fine with removing all of them in one go.
Anyway, I have removed them all in
https://lore.kernel.org/r/[email protected]/
Does it look good to you?
Thanks,
Gao Xiang
>
> regards,
> dan carpenter
>
On Fri, 2019-08-30 at 00:44 +0800, Gao Xiang wrote:
> Hi Dan,
>
> On Thu, Aug 29, 2019 at 11:43:46PM +0800, Dan Carpenter wrote:
> > > p.s. There are 2947 (un)likely places in fs/ directory.
> >
> > I was complaining about you adding new pointless ones, not existing
> > ones. The likely/unlikely annotations are supposed to be functional and
> > not decorative. I explained this very clearly.
> >
> > Probably most of the annotations in fs/ are wrong but they are also
> > harmless except for the slight messiness. However there are definitely
> > some which are important so removing them all isn't a good idea.
> >
> > > If you like, I will delete them all.
> >
> > But for erofs, I don't think that any of the likely/unlikely calls have
> > been thought about so I'm fine with removing all of them in one go.
>
> Anyway, I have removed them all in
> https://lore.kernel.org/r/[email protected]/
>
> Does it look good to you?
Unrelated bikeshed from a trivial look:
There's a block there that looks like:
diff --git a/fs/erofs/data.c b/fs/erofs/data.c
[]
@@ -70,7 +70,7 @@ struct page *__erofs_get_meta_page(struct super_block *sb,
}
err = bio_add_page(bio, page, PAGE_SIZE, 0);
- if (unlikely(err != PAGE_SIZE)) {
+ if (err != PAGE_SIZE) {
err = -EFAULT;
goto err_out;
}
The initial assignment to err is odd as it's not
actually an error value -E<FOO> but a int size
from a unsigned int len.
Here the return is either 0 or PAGE_SIZE.
This would be more legible to me as:
if (bio_add_page(bio, page, PAGE_SIZE, 0) != PAGE_SIZE) {
err = -EFAULT;
goto err_out;
}
Hi Joe,
On Thu, Aug 29, 2019 at 09:59:21AM -0700, Joe Perches wrote:
> On Fri, 2019-08-30 at 00:44 +0800, Gao Xiang wrote:
> > Hi Dan,
> >
> > On Thu, Aug 29, 2019 at 11:43:46PM +0800, Dan Carpenter wrote:
> > > > p.s. There are 2947 (un)likely places in fs/ directory.
> > >
> > > I was complaining about you adding new pointless ones, not existing
> > > ones. The likely/unlikely annotations are supposed to be functional and
> > > not decorative. I explained this very clearly.
> > >
> > > Probably most of the annotations in fs/ are wrong but they are also
> > > harmless except for the slight messiness. However there are definitely
> > > some which are important so removing them all isn't a good idea.
> > >
> > > > If you like, I will delete them all.
> > >
> > > But for erofs, I don't think that any of the likely/unlikely calls have
> > > been thought about so I'm fine with removing all of them in one go.
> >
> > Anyway, I have removed them all in
> > https://lore.kernel.org/r/[email protected]/
> >
> > Does it look good to you?
>
> Unrelated bikeshed from a trivial look:
>
> There's a block there that looks like:
>
> diff --git a/fs/erofs/data.c b/fs/erofs/data.c
> []
> @@ -70,7 +70,7 @@ struct page *__erofs_get_meta_page(struct super_block *sb,
> }
>
> err = bio_add_page(bio, page, PAGE_SIZE, 0);
> - if (unlikely(err != PAGE_SIZE)) {
> + if (err != PAGE_SIZE) {
> err = -EFAULT;
> goto err_out;
> }
>
> The initial assignment to err is odd as it's not
> actually an error value -E<FOO> but a int size
> from a unsigned int len.
>
> Here the return is either 0 or PAGE_SIZE.
>
> This would be more legible to me as:
>
> if (bio_add_page(bio, page, PAGE_SIZE, 0) != PAGE_SIZE) {
> err = -EFAULT;
> goto err_out;
> }
Okay, that is more reasonable, I will update the original patch as you suggested.
Thanks,
Gao Xiang
>
>
On Wednesday 28 August 2019 18:08:17 Greg Kroah-Hartman wrote:
> From: Valdis Klētnieks <[email protected]>
>
> The exfat code needs a lot of work to get it into "real" shape for
> the fs/ part of the kernel, so put it into drivers/staging/ for now so
> that it can be worked on by everyone in the community.
>
> The full specification of the filesystem can be found at:
> https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification
>
> Signed-off-by: Valdis Kletnieks <[email protected]>
> Signed-off-by: Sasha Levin <[email protected]>
> Signed-off-by: Greg Kroah-Hartman <[email protected]>
Hi Greg!
I'm not really sure if this exfat implementation is fully suitable for
mainline linux kernel.
In my opinion, proper way should be to implement exFAT support into
existing fs/fat/ code instead of replacing whole vfat/msdosfs by this
new (now staging) fat implementation.
In linux kernel we really do not need two different implementation of
VFAT32.
So I'm a bit sceptical about usefulness of this exfat code/driver, if it
makes any sense to have it even in staging.
--
Pali Rohár
[email protected]
On Thu, 29 Aug 2019 22:56:31 +0200, Pali Roh?r said:
> I'm not really sure if this exfat implementation is fully suitable for
> mainline linux kernel.
>
> In my opinion, proper way should be to implement exFAT support into
> existing fs/fat/ code instead of replacing whole vfat/msdosfs by this
> new (now staging) fat implementation.
> In linux kernel we really do not need two different implementation of
> VFAT32.
This patch however does have one major advantage over "patch vfat to
support exfat" - which is that the patch exists.
If somebody comes forward with an actual "extend vfat to do exfat" patch,
we should at that point have a discussion about relative merits....
On Thu, Aug 29, 2019 at 07:18:16PM -0400, Valdis Klētnieks wrote:
>On Thu, 29 Aug 2019 22:56:31 +0200, Pali Roh?r said:
>
>> I'm not really sure if this exfat implementation is fully suitable for
>> mainline linux kernel.
>>
>> In my opinion, proper way should be to implement exFAT support into
>> existing fs/fat/ code instead of replacing whole vfat/msdosfs by this
>> new (now staging) fat implementation.
>
>> In linux kernel we really do not need two different implementation of
>> VFAT32.
>
>This patch however does have one major advantage over "patch vfat to
>support exfat" - which is that the patch exists.
>
>If somebody comes forward with an actual "extend vfat to do exfat" patch,
>we should at that point have a discussion about relative merits....
This patch going into staging doesn't necessarily mean that one day
it'll get moved to fs/exfat/. It's very possible that the approach would
instead be to use the staging code for reference, build this
functionality in fs/fat/, and kill off the staging code when it's not
needed anymore.
With regards to missing specs/docs/whatever - our main concern with this
release was that we want full interoperability, which is why the spec
was made public as-is without modifications from what was used
internally. There's no "secret sauce" that Microsoft is hiding here.
How about we give this spec/code time to get soaked and reviewed for a
bit, and if folks still feel (in a month or so?) that there are missing
bits of information related to exfat, I'll be happy to go back and try
to get them out as well.
--
Thanks,
Sasha
On 2019/8/29 23:43, Dan Carpenter wrote:
>> p.s. There are 2947 (un)likely places in fs/ directory.
>
> I was complaining about you adding new pointless ones, not existing
> ones. The likely/unlikely annotations are supposed to be functional and
> not decorative. I explained this very clearly.
>
> Probably most of the annotations in fs/ are wrong but they are also
> harmless except for the slight messiness. However there are definitely
> some which are important so removing them all isn't a good idea.
Hi Dan,
Could you please pick up one positive example using likely and unlikely
correctly? so we can follow the example, rather than removing them all blindly.
Thanks,
>
>> If you like, I will delete them all.
>
> But for erofs, I don't think that any of the likely/unlikely calls have
> been thought about so I'm fine with removing all of them in one go.
>
> regards,
> dan carpenter
>
> .
>
Hi Dan,
On Fri, Aug 30, 2019 at 10:06:25AM +0800, Chao Yu wrote:
> On 2019/8/29 23:43, Dan Carpenter wrote:
> >> p.s. There are 2947 (un)likely places in fs/ directory.
> >
> > I was complaining about you adding new pointless ones, not existing
> > ones. The likely/unlikely annotations are supposed to be functional and
> > not decorative. I explained this very clearly.
> >
> > Probably most of the annotations in fs/ are wrong but they are also
> > harmless except for the slight messiness. However there are definitely
> > some which are important so removing them all isn't a good idea.
>
> Hi Dan,
>
> Could you please pick up one positive example using likely and unlikely
> correctly? so we can follow the example, rather than removing them all blindly.
I'm also curious about that, what is the filesystem or kernel standard about
likely/unlikely use (since I didn't find some documented standard so I used
in my personal way, I think it is reasonable at least to cover all error
handling paths), maybe I'm an _idiot_ as some earlier unfriendly word said
somewhere so I'm too stupid to understand the implicit meaning of some document.
>
> Thanks,
>
> >
> >> If you like, I will delete them all.
> >
> > But for erofs, I don't think that any of the likely/unlikely calls have
> > been thought about so I'm fine with removing all of them in one go.
Add a word (just a note), I don't think such kind of "any", "few", "all"
words are meaningful without some explicit evidence. (e.g. such as EROFS
have few error handling path. I don't think that is true and worth to
give many details since EROFS code is here for more than one year.)
Yes, EROFS is not prefectly, I have to admit, and I said similar words
on other threads for many times if you decide to check each likely/unlikely
line by line, I cannot say all unlikely/likely cases I wrote are reasonable
(just as bug-free, I think no one can make such guarantee even for new code),
but I can say the majority of them are reasonable in my personal understanding
of likely/unlikely. And I can fix all your reports in time (but maybe some
are not urgent at all.)
In addition there will be endless discussions on detailed code since there are
many personal tendencies from various people in it, as the saying goes "There
are a thousand Hamlets in a thousand people's eyes. "
Anyway, I have sent a patch to kill them all blindly as you like, so I think
we can come to an agreement on it, but I still don't fully agree with your
"for EROFS, I don't think that any of the likely/unlikely calls have been
thought about" conclusion.
Thanks,
Gao Xiang
> >
> > regards,
> > dan carpenter
> >
> > .
> >
Hello, thank you for input!
On Thursday 29 August 2019 19:35:06 Sasha Levin wrote:
> On Thu, Aug 29, 2019 at 07:18:16PM -0400, Valdis Klētnieks wrote:
> > On Thu, 29 Aug 2019 22:56:31 +0200, Pali Roh?r said:
> >
> > > I'm not really sure if this exfat implementation is fully suitable for
> > > mainline linux kernel.
> > >
> > > In my opinion, proper way should be to implement exFAT support into
> > > existing fs/fat/ code instead of replacing whole vfat/msdosfs by this
> > > new (now staging) fat implementation.
> >
> > > In linux kernel we really do not need two different implementation of
> > > VFAT32.
> >
> > This patch however does have one major advantage over "patch vfat to
> > support exfat" - which is that the patch exists.
> >
> > If somebody comes forward with an actual "extend vfat to do exfat" patch,
> > we should at that point have a discussion about relative merits....
>
> This patch going into staging doesn't necessarily mean that one day
> it'll get moved to fs/exfat/. It's very possible that the approach would
> instead be to use the staging code for reference, build this
> functionality in fs/fat/, and kill off the staging code when it's not
> needed anymore.
So, if current exfat code is just going to be "reference" how to
implement exfat properly in fs/fat, is this correct usage of "staging"?
I was in impression that staging is there for drivers which needs
cleanup as they are not ready to be part of mainline. But not that it is
for "random" implementation of something which is going to be just a
"code example" or "reference" for final implementation which would be
different.
Maybe other people could clarify state of staging, if this is how
staging should be used or not.
> With regards to missing specs/docs/whatever - our main concern with this
> release was that we want full interoperability, which is why the spec
> was made public as-is without modifications from what was used
> internally. There's no "secret sauce" that Microsoft is hiding here.
Ok, if it was just drop of "current version" of documentation then it
makes sense.
> How about we give this spec/code time to get soaked and reviewed for a
> bit, and if folks still feel (in a month or so?) that there are missing
> bits of information related to exfat, I'll be happy to go back and try
> to get them out as well.
Basically external references in that released exFAT specification are
unknown / not released yet. Like TexFAT. So if you have an input in MS
could you forward request about these missing bits?
Also, MS already released source code of FAT32 kernel driver
(fastfat.sys) and from code can be seen that it does not match MS FAT
specification, and include some "secret sauce" bits. Source code is on:
https://github.com/microsoft/Windows-driver-samples/tree/master/filesys/fastfat
In fastfat.sys there is e.g. usage of undocumented bits in directory
entry or usage of undocumented bits for marking filesystem as "dirty" --
incorrectly unmounted.
Would it be possible for MS to release in same way code of exFAT NT
driver? I'm really a bit sceptical that exFAT MS implementation is
really according to standard as I already known that FAT32 is not.
Just to note that I did some investigation in FAT32 level how are
handled volume labels and I found more bugs in current implementations
(mtools, dosfstools, util-linux and even in fastfat.sys). Some
references are on: https://www.spinics.net/lists/kernel/msg2640891.html
Fixes for mtools, dosfstools and util-linux were already applied and fix
for MS's fastfat.sys is pending in opened pull request:
https://github.com/microsoft/Windows-driver-samples/pull/303
Could you please forward my pull request for fastfat.sys fixes to
appropriate people/team in MS?
--
Pali Rohár
[email protected]
On Thursday 29 August 2019 19:18:16 Valdis Klētnieks wrote:
> On Thu, 29 Aug 2019 22:56:31 +0200, Pali Roh?r said:
>
> > I'm not really sure if this exfat implementation is fully suitable for
> > mainline linux kernel.
> >
> > In my opinion, proper way should be to implement exFAT support into
> > existing fs/fat/ code instead of replacing whole vfat/msdosfs by this
> > new (now staging) fat implementation.
>
> > In linux kernel we really do not need two different implementation of
> > VFAT32.
>
> This patch however does have one major advantage over "patch vfat to
> support exfat" - which is that the patch exists.
I understand that this is advantage...
> If somebody comes forward with an actual "extend vfat to do exfat" patch,
> we should at that point have a discussion about relative merits....
... but is this advantage such big that it should be merged even due to
"horrible" code quality and lot of code/functionality duplication?
In similar way there should be discussion about these pros and cons.
--
Pali Rohár
[email protected]
On Fri, Aug 30, 2019 at 12:04:41AM +0800, Gao Xiang wrote:
> Anyway, I'm fine to delete them all if you like, but I think majority of these
> are meaningful.
>
> data.c- /* page is already locked */
> data.c- DBG_BUGON(PageUptodate(page));
> data.c-
> data.c: if (unlikely(err))
> data.c- SetPageError(page);
> data.c- else
> data.c- SetPageUptodate(page);
If we cared about speed here then we would delete the DBG_BUGON() check
because that's going to be expensive. The likely/unlikely annotations
should be used in places a reasonable person thinks it will make a
difference to benchmarks.
regards,
dan carpenter
Hi Dan,
On Fri, Aug 30, 2019 at 11:34:45AM +0300, Dan Carpenter wrote:
> On Fri, Aug 30, 2019 at 12:04:41AM +0800, Gao Xiang wrote:
> > Anyway, I'm fine to delete them all if you like, but I think majority of these
> > are meaningful.
> >
> > data.c- /* page is already locked */
> > data.c- DBG_BUGON(PageUptodate(page));
> > data.c-
> > data.c: if (unlikely(err))
> > data.c- SetPageError(page);
> > data.c- else
> > data.c- SetPageUptodate(page);
>
> If we cared about speed here then we would delete the DBG_BUGON() check
> because that's going to be expensive. The likely/unlikely annotations
> should be used in places a reasonable person thinks it will make a
> difference to benchmarks.
DBG_BUGON will be a no-op ((void)x) in non-debugging mode,
I discussed related stuffs with Greg many months before [1] and
other filesystems also have similar functions...
p.s. I think we come to an agreement here...
I killed all unlikely/likely.
[1] http://driverdev.linuxdriverproject.org/pipermail/driverdev-devel/2018-November/128951.html
sorry about no lore here.
Thanks,
Gao Xiang
>
> regards,
> dan carpenter
>
On Fri, Aug 30, 2019 at 04:43:33PM +0800, Gao Xiang wrote:
> Hi Dan,
>
> On Fri, Aug 30, 2019 at 11:34:45AM +0300, Dan Carpenter wrote:
> > On Fri, Aug 30, 2019 at 12:04:41AM +0800, Gao Xiang wrote:
> > > Anyway, I'm fine to delete them all if you like, but I think majority of these
> > > are meaningful.
> > >
> > > data.c- /* page is already locked */
> > > data.c- DBG_BUGON(PageUptodate(page));
> > > data.c-
> > > data.c: if (unlikely(err))
> > > data.c- SetPageError(page);
> > > data.c- else
> > > data.c- SetPageUptodate(page);
> >
> > If we cared about speed here then we would delete the DBG_BUGON() check
> > because that's going to be expensive. The likely/unlikely annotations
> > should be used in places a reasonable person thinks it will make a
> > difference to benchmarks.
>
> DBG_BUGON will be a no-op ((void)x) in non-debugging mode,
It expands to:
((void)PageUptodate(page));
Calling PageUptodate() doesn't do anything, but it isn't free. The
time it takes to do that function call completely negates any speed up
from using likely/unlikely.
I'm really not trying to be a jerk...
regards,
dan carpenter
On Fri, Aug 30, 2019 at 10:06:25AM +0800, Chao Yu wrote:
> On 2019/8/29 23:43, Dan Carpenter wrote:
> >> p.s. There are 2947 (un)likely places in fs/ directory.
> >
> > I was complaining about you adding new pointless ones, not existing
> > ones. The likely/unlikely annotations are supposed to be functional and
> > not decorative. I explained this very clearly.
> >
> > Probably most of the annotations in fs/ are wrong but they are also
> > harmless except for the slight messiness. However there are definitely
> > some which are important so removing them all isn't a good idea.
>
> Hi Dan,
>
> Could you please pick up one positive example using likely and unlikely
> correctly? so we can follow the example, rather than removing them all blindly.
Remove all of them and re-add with explanation if and how each is going
to make things better. If you can't reason about, prove by benchmarks or
point to inefficient asm code generated, then don't add them again.
The feedback I got from CPU and compiler people over the years is not to
bother using the annotations. CPUs are doing dynamic branch prediction
and compilers are applying tons of optimizations.
GCC docs state about the builtin: "In general, you should prefer to use
actual profile feedback for this (-fprofile-arcs), as programmers are
notoriously bad at predicting how their programs actually perform."
(https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html)
> I'm also curious about that, what is the filesystem or kernel standard about
> likely/unlikely use (since I didn't find some documented standard
> so I used in my personal way,
Such information is helpful.
> I think it is reasonable at least to cover all error handling paths),
I hope so, too.
I imagine that the likeliness annotation usage could depend also on
software build parameters.
Would you occasionally like to influence corresponding probabilities
any more by system settings?
> maybe I'm an _idiot_
I hope not.
> as some earlier unfriendly word said somewhere
> so I'm too stupid to understand the implicit meaning of some document.
Can the software development discussion be continued in more constructive ways?
Regards,
Markus
Hi Dan,
On Fri, Aug 30, 2019 at 02:26:12PM +0300, Dan Carpenter wrote:
> On Fri, Aug 30, 2019 at 04:43:33PM +0800, Gao Xiang wrote:
> > Hi Dan,
> >
> > On Fri, Aug 30, 2019 at 11:34:45AM +0300, Dan Carpenter wrote:
> > > On Fri, Aug 30, 2019 at 12:04:41AM +0800, Gao Xiang wrote:
> > > > Anyway, I'm fine to delete them all if you like, but I think majority of these
> > > > are meaningful.
> > > >
> > > > data.c- /* page is already locked */
> > > > data.c- DBG_BUGON(PageUptodate(page));
> > > > data.c-
> > > > data.c: if (unlikely(err))
> > > > data.c- SetPageError(page);
> > > > data.c- else
> > > > data.c- SetPageUptodate(page);
> > >
> > > If we cared about speed here then we would delete the DBG_BUGON() check
> > > because that's going to be expensive. The likely/unlikely annotations
> > > should be used in places a reasonable person thinks it will make a
> > > difference to benchmarks.
> >
> > DBG_BUGON will be a no-op ((void)x) in non-debugging mode,
>
> It expands to:
>
> ((void)PageUptodate(page));
>
> Calling PageUptodate() doesn't do anything, but it isn't free. The
> time it takes to do that function call completely negates any speed up
> from using likely/unlikely.
>
> I'm really not trying to be a jerk...
You are right, I recalled that PageUptodate is not as simple as it implys.
Yes, those are all removed now... I am ok with that,
thanks for your suggestion :)
Thanks,
Gao Xiang
>
> regards,
> dan carpenter
>
On Thu, Aug 29, 2019 at 01:18:10PM +0200, Greg Kroah-Hartman wrote:
> Hey, that's not nice, erofs isn't a POS. It could always use more
> review, which the developers asked for numerous times.
>
> There's nothing different from a filesystem compared to a driver. If
> its stand-alone, and touches nothing else, all issues with it are
> self-contained and do not bother anyone else in the kernel. We merge
> drivers all the time that need more work because our review cycles are
> low. And review cycles for vfs developers are even more scarce than
> driver reviewers.
A lot of the issue that are trivial to pick are really just very basic
issue that don't even require file system know how. Or in other ways
just a little less lazy developer that looks out for similar code
outside their own little fiefdom.
On Thu, Aug 29, 2019 at 10:56:31PM +0200, Pali Roh?r wrote:
> In my opinion, proper way should be to implement exFAT support into
> existing fs/fat/ code instead of replacing whole vfat/msdosfs by this
> new (now staging) fat implementation.
>
> In linux kernel we really do not need two different implementation of
> VFAT32.
Not only not useful, but having another one is actively harmful, as
people might actually accidentally used it for classic fat.
But what I'm really annoyed at is this whole culture of just dumping
some crap into staging and hoping it'll sort itself out. Which it
won't. We'll need a dedidcated developer spending some time on it
and just get it into shape, and having it in staging does not help
with that at all - it will get various random cleanup that could
be trivially scripted, but that is rarely the main issue with any
codebase.
On Friday 30 August 2019 08:40:06 Christoph Hellwig wrote:
> On Thu, Aug 29, 2019 at 10:56:31PM +0200, Pali Rohár wrote:
> > In my opinion, proper way should be to implement exFAT support into
> > existing fs/fat/ code instead of replacing whole vfat/msdosfs by this
> > new (now staging) fat implementation.
> >
> > In linux kernel we really do not need two different implementation of
> > VFAT32.
>
> Not only not useful, but having another one is actively harmful, as
> people might actually accidentally used it for classic fat.
>
> But what I'm really annoyed at is this whole culture of just dumping
> some crap into staging and hoping it'll sort itself out. Which it
> won't. We'll need a dedidcated developer spending some time on it
> and just get it into shape, and having it in staging does not help
> with that at all - it will get various random cleanup that could
> be trivially scripted, but that is rarely the main issue with any
> codebase.
Exactly. Somebody should take this code and work on it. Otherwise we can
say it is dead code and would happen same thing as with other staging
drivers -- ready to be removed.
--
Pali Rohár
[email protected]
On Thu, Aug 29, 2019 at 01:18:10PM +0200, Greg Kroah-Hartman wrote:
> On Thu, Aug 29, 2019 at 03:37:49AM -0700, Christoph Hellwig wrote:
> > On Thu, Aug 29, 2019 at 11:50:19AM +0200, Greg Kroah-Hartman wrote:
> > > I know the code is horrible, but I will gladly take horrible code into
> > > staging. If it bothers you, just please ignore it. That's what staging
> > > is there for :)
> >
> > And then after a while you decide it's been long enough and force move
> > it out of staging like the POS erofs code?
>
> Hey, that's not nice, erofs isn't a POS. It could always use more
> review, which the developers asked for numerous times.
>
> There's nothing different from a filesystem compared to a driver. If
> its stand-alone, and touches nothing else, all issues with it are
> self-contained and do not bother anyone else in the kernel. We merge
I whole-heartedly disagree with that statement.
The major difference between filesystems and the rest of the kernel
that seems to be missed by most kernel developers is that
filesystems maintain persistent data - you can't fix a problem/bug
by rebooting or power cycling. Once the filesystem code screws up,
the user is left with a mess they have to fix and that invariably
results in data loss.
Users remember when a filesystem eats their data - they don't tend
to want to have anything to do with that filesystem ever again if it
happens to them. We still get people saying "XFS ate my data back in
2002, I dont trust it and I'll never use it again".
Users put up with shit hardware and drivers - it's an inconvenience
more than anything. They don't put up with buggy filesystems that
lose their data - that is completely unacceptible to users. As a
result, the quality and stability standard for merging a new
filesystem needs to be far higher that what is acceptible for
merging a new driver.
The correct place for new filesystem review is where all the
experienced filesystem developers hang out - that's linux-fsdevel,
not the driver staging tree.
Cheers,
Dave.
--
Dave Chinner
[email protected]
On 2019/8/30 19:51, David Sterba wrote:
> On Fri, Aug 30, 2019 at 10:06:25AM +0800, Chao Yu wrote:
>> On 2019/8/29 23:43, Dan Carpenter wrote:
>>>> p.s. There are 2947 (un)likely places in fs/ directory.
>>>
>>> I was complaining about you adding new pointless ones, not existing
>>> ones. The likely/unlikely annotations are supposed to be functional and
>>> not decorative. I explained this very clearly.
>>>
>>> Probably most of the annotations in fs/ are wrong but they are also
>>> harmless except for the slight messiness. However there are definitely
>>> some which are important so removing them all isn't a good idea.
>>
>> Hi Dan,
>>
>> Could you please pick up one positive example using likely and unlikely
>> correctly? so we can follow the example, rather than removing them all blindly.
>
> Remove all of them and re-add with explanation if and how each is going
> to make things better. If you can't reason about, prove by benchmarks or
> point to inefficient asm code generated, then don't add them again.
It seems the result of it is strongly related to arch and compiler, if we readd
it after we just prove its gain only in one combination, I doubt we may suffer
regression in other combination in between archs and comilers. It looks like we
don't have any cheaper way to readd it? instead of verifying all/most combinations.
>
> The feedback I got from CPU and compiler people over the years is not to
> bother using the annotations. CPUs are doing dynamic branch prediction
> and compilers are applying tons of optimizations.
>
> GCC docs state about the builtin: "In general, you should prefer to use
> actual profile feedback for this (-fprofile-arcs), as programmers are
> notoriously bad at predicting how their programs actually perform."
> (https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html)
Yes, I agree with this. Thanks a lot for sharing your experience. :)
The removal change has done and been merged into Greg's tree, let's consider to
readd it later if possible as you suggested.
thanks,
> .
>
On Sat, 31 Aug 2019 07:54:10 +1000, Dave Chinner said:
> The correct place for new filesystem review is where all the
> experienced filesystem developers hang out - that's linux-fsdevel,
> not the driver staging tree.
So far everything's been cc'ed to linux-fsdevel, which has been spending
more time discussing unlikely() usage in a different filesystem.
On Sat, Aug 31, 2019 at 06:31:45AM -0400, Valdis Klētnieks wrote:
> On Sat, 31 Aug 2019 07:54:10 +1000, Dave Chinner said:
>
> > The correct place for new filesystem review is where all the
> > experienced filesystem developers hang out - that's linux-fsdevel,
> > not the driver staging tree.
>
> So far everything's been cc'ed to linux-fsdevel, which has been spending
> more time discussing unlikely() usage in a different filesystem.
That's just noise - you'll get whitespace and other trivial
review on any list you post a patch series for review. Go back and
look at what other people have raised w.r.t. to that filesystem -
on-disk format validation, re-implementation of largely generic
code, lack of namespacing of functions leading to conflicts with
generic/VFS functionality, etc.
Review bandwidth for things like on-disk format definition and
manipulation, consistency with other filesystems, efficient
integration into the generic infrastructure, etc is limited.
Everyone has to juggle that around the load they have for their own
filesystem maintenance, and there's usually only bandwidth for a
single filesystem at a time.
Just be patient - trying to force the merging of code before there's
even been consensus on fundamental architecture choices doesn't make
things better for anyone. Merging incomplete filesystem code early
in the development cycle has -always- been something we've regretted
in the long run. We've learn this lesson many times before, yet we
seem doomed to repeat it yet again...
Cheers,
Dave.
--
Dave Chinner
[email protected]
Hi.
I just noticed that this exfat-staging drivers are based on the old
Samsung's 1.x exFAT drivers.
I've been working to get the newer Samsung's driver(now named "sdFAT")
to fit better for general Linux users, and I believe it can provide a
better base for the community to work on(and hopefully complies better
to the mainline coding standard).
GitHub link
https://github.com/arter97/exfat-linux
I also included some rudimentary benchmark results.
I encourage mainline developers to explore this driver base and see if
it's worth to switch, since it's the early days of exfat-staging.
To others watching this thread:
It's more than likely that you can start using exFAT reliably right
away by following the link above. It's tested on all major LTS kernels
ranging from 3.4 to 4.19 and the ones Canonical uses for Ubuntu: 3.4,
3.10, 3.18, 4.1, 4.4, 4.9, 4.14, 4.19 and 4.15, 5.0, 5.2, and 5.3-rc.
Thanks.
On Sat, Sep 14, 2019 at 10:39:51PM +0900, Park Ju Hyung wrote:
> Hi.
>
> I just noticed that this exfat-staging drivers are based on the old
> Samsung's 1.x exFAT drivers.
>
> I've been working to get the newer Samsung's driver(now named "sdFAT")
> to fit better for general Linux users, and I believe it can provide a
> better base for the community to work on(and hopefully complies better
> to the mainline coding standard).
>
> GitHub link
> https://github.com/arter97/exfat-linux
>
> I also included some rudimentary benchmark results.
>
> I encourage mainline developers to explore this driver base and see if
> it's worth to switch, since it's the early days of exfat-staging.
Note, this just showed up publically on August 12, where were you with
all of this new code before then? :)
> To others watching this thread:
> It's more than likely that you can start using exFAT reliably right
> away by following the link above. It's tested on all major LTS kernels
> ranging from 3.4 to 4.19 and the ones Canonical uses for Ubuntu: 3.4,
> 3.10, 3.18, 4.1, 4.4, 4.9, 4.14, 4.19 and 4.15, 5.0, 5.2, and 5.3-rc.
For the in-kernel code, we would have to rip out all of the work you did
for all older kernels, so that's a non-starter right there.
As for what codebase to work off of, I don't want to say it is too late,
but really, this shows up from nowhere and we had to pick something so
we found the best we could at that point in time.
Is there anything specific in the codebase you have now, that is lacking
in the in-kernel code? Old-kernel-support doesn't count here, as we
don't care about that as it is not applicable. But functionality does
matter, what has been added here that we can make use of?
And do you have any "real" development history to look at instead of the
"one giant commit" of the initial code drop? That is where we could
actually learn what has changed over time. Your repo as-is shows none
of the interesting bits :(
thanks,
greg kh
Hi Greg,
On Sun, Sep 15, 2019 at 10:54 PM Greg KH <[email protected]> wrote:
> Note, this just showed up publically on August 12, where were you with
> all of this new code before then? :)
My sdFAT port, exfat-nofuse and the one on the staging tree, were all
made by Samsung.
And unless you guys had a chance to talk to Samsung developers
directly, those all share the same faith of lacking proper development
history.
The source I used was from http://opensource.samsung.com, which
provides kernel sources as tar.gz files.
There is no code history available.
> For the in-kernel code, we would have to rip out all of the work you did
> for all older kernels, so that's a non-starter right there.
I'm aware.
I'm just letting mainline know that there is potentially another (much
better) base that could be upstreamed.
If you want me to rip out older kernel support for upstreaming, I'm
more than happy to do so.
> As for what codebase to work off of, I don't want to say it is too late,
> but really, this shows up from nowhere and we had to pick something so
> we found the best we could at that point in time.
To be honest, whole public exFAT sources are all from nowhere unless
you had internal access to Samsung's development archive.
The one in the current staging tree isn't any better.
I'm not even sure where the staging driver is from, actually.
Samsung used the 1.2.x versioning until they switched to a new code
base - sdFAT.
The one in the staging tree is marked version 1.3.0(exfat_super.c).
I failed to find anything 1.3.x from Samsung's public kernel sources.
The last time exFAT 1.2.x was used was in Galaxy S7(released in 2016).
Mine was originally based on sdFAT 2.1.10, used in Galaxy S10(released
in March 2019) and it just got updated to 2.2.0, used in Galaxy
Note10(released in August 2019).
> Is there anything specific in the codebase you have now, that is lacking
> in the in-kernel code? Old-kernel-support doesn't count here, as we
> don't care about that as it is not applicable. But functionality does
> matter, what has been added here that we can make use of?
This is more of a suggestion of
"Let's base on a *much more recent* snapshot for the community to work on",
since the current one on the staging tree also lacks development history.
The diff is way too big to even start understanding the difference.
With that said though, I do have some vague but real reason as to why
sdFAT base is better.
With some major Android vendors showing interests in supporting exFAT,
Motorola notably published their work on public Git repository with
full development history(the only vendor to do this that I'm aware
of).
Commits like this:
https://github.com/MotorolaMobilityLLC/kernel-msm/commit/7ab1657 is
not merged to exFAT(including the current staging tree one) while it
did for sdFAT.
The only thing I regret is not working on porting sdFAT sooner.
I definitely didn't anticipate Microsoft to suddenly lift legal issues
on upstreaming exFAT just around when I happen to gain interest in
porting sdFAT.
If my port happened sooner, it would have been a no-brainer for it to
be considered as a top candidate for upstreaming.
> And do you have any "real" development history to look at instead of the
> "one giant commit" of the initial code drop? That is where we could
> actually learn what has changed over time. Your repo as-is shows none
> of the interesting bits :(
As I mentioned, development history is unobtainable, even for the
current staging tree or exfat-nofuse.
(If you guys took exfat-nofuse, you can also see that there's barely
any real exFAT-related development done in that tree. Everything is
basically fixes for newer kernel versions.)
The best I could do, if someone's interested, is to diff all versions
of exFAT/sdFAT throughout the Samsung's kernel versions, but that
still won't give us reasons as to why the changes were made.
TL;DR
My suggestion - Let's base on a much newer driver that's matured more,
contains more fixes, gives (slightly?) better performance and
hopefully has better code quality.
Both drivers are horrible.
You said it yourself(for the current staging one), and even for my new
sdFAT-base proposal, I'm definitely not comfortable seeing this kind
of crap in mainline:
https://github.com/arter97/exfat-linux/commit/0f1ddde
However, it's clear to me that the sdFAT base is less-horrible.
Please let me know what you think.
> thanks,
>
> greg kh
Thanks.
On Friday 30 August 2019 09:56:47 Pali Rohár wrote:
> On Thursday 29 August 2019 19:35:06 Sasha Levin wrote:
> > With regards to missing specs/docs/whatever - our main concern with this
> > release was that we want full interoperability, which is why the spec
> > was made public as-is without modifications from what was used
> > internally. There's no "secret sauce" that Microsoft is hiding here.
>
> Ok, if it was just drop of "current version" of documentation then it
> makes sense.
>
> > How about we give this spec/code time to get soaked and reviewed for a
> > bit, and if folks still feel (in a month or so?) that there are missing
> > bits of information related to exfat, I'll be happy to go back and try
> > to get them out as well.
Hello Sasha!
Now one month passed, so do you have some information when missing parts
of documentation like TexFAT would be released to public?
> Basically external references in that released exFAT specification are
> unknown / not released yet. Like TexFAT. So if you have an input in MS
> could you forward request about these missing bits?
--
Pali Rohár
[email protected]
On Wed, Oct 16, 2019 at 04:03:53PM +0200, Pali Roh?r wrote:
>On Friday 30 August 2019 09:56:47 Pali Roh?r wrote:
>> On Thursday 29 August 2019 19:35:06 Sasha Levin wrote:
>> > With regards to missing specs/docs/whatever - our main concern with this
>> > release was that we want full interoperability, which is why the spec
>> > was made public as-is without modifications from what was used
>> > internally. There's no "secret sauce" that Microsoft is hiding here.
>>
>> Ok, if it was just drop of "current version" of documentation then it
>> makes sense.
>>
>> > How about we give this spec/code time to get soaked and reviewed for a
>> > bit, and if folks still feel (in a month or so?) that there are missing
>> > bits of information related to exfat, I'll be happy to go back and try
>> > to get them out as well.
>
>Hello Sasha!
>
>Now one month passed, so do you have some information when missing parts
>of documentation like TexFAT would be released to public?
Sure, I'll see if I can get an approval to open it up.
Can I assume you will be implementing TexFAT support once the spec is
available?
--
Thanks,
Sasha
On Wednesday 16 October 2019 10:31:13 Sasha Levin wrote:
> On Wed, Oct 16, 2019 at 04:03:53PM +0200, Pali Rohár wrote:
> > On Friday 30 August 2019 09:56:47 Pali Rohár wrote:
> > > On Thursday 29 August 2019 19:35:06 Sasha Levin wrote:
> > > > With regards to missing specs/docs/whatever - our main concern with this
> > > > release was that we want full interoperability, which is why the spec
> > > > was made public as-is without modifications from what was used
> > > > internally. There's no "secret sauce" that Microsoft is hiding here.
> > >
> > > Ok, if it was just drop of "current version" of documentation then it
> > > makes sense.
> > >
> > > > How about we give this spec/code time to get soaked and reviewed for a
> > > > bit, and if folks still feel (in a month or so?) that there are missing
> > > > bits of information related to exfat, I'll be happy to go back and try
> > > > to get them out as well.
> >
> > Hello Sasha!
> >
> > Now one month passed, so do you have some information when missing parts
> > of documentation like TexFAT would be released to public?
>
> Sure, I'll see if I can get an approval to open it up.
Ok!
> Can I assume you will be implementing TexFAT support once the spec is
> available?
I cannot promise that I would implement something which I do not know
how is working... It depends on how complicated TexFAT is and also how
future exfat support in kernel would look like.
But I'm interesting in implementing it.
--
Pali Rohár
[email protected]
On Wed, 16 Oct 2019 10:31:13 -0400, Sasha Levin said:
> On Wed, Oct 16, 2019 at 04:03:53PM +0200, Pali Roh?r wrote:
> >Now one month passed, so do you have some information when missing parts
> >of documentation like TexFAT would be released to public?
>
> Sure, I'll see if I can get an approval to open it up.
>
> Can I assume you will be implementing TexFAT support once the spec is
> available?
It's certainly something that *should* be supported. The exact timeframe, and
who the "you" that actually writes the patch is of course up in the air (and
will likely end up being a collaborative effort between the first author and
corrections from others).
On Wed, Oct 16, 2019 at 06:03:49PM +0200, Pali Roh?r wrote:
>On Wednesday 16 October 2019 10:31:13 Sasha Levin wrote:
>> On Wed, Oct 16, 2019 at 04:03:53PM +0200, Pali Roh?r wrote:
>> > On Friday 30 August 2019 09:56:47 Pali Roh?r wrote:
>> > > On Thursday 29 August 2019 19:35:06 Sasha Levin wrote:
>> > > > With regards to missing specs/docs/whatever - our main concern with this
>> > > > release was that we want full interoperability, which is why the spec
>> > > > was made public as-is without modifications from what was used
>> > > > internally. There's no "secret sauce" that Microsoft is hiding here.
>> > >
>> > > Ok, if it was just drop of "current version" of documentation then it
>> > > makes sense.
>> > >
>> > > > How about we give this spec/code time to get soaked and reviewed for a
>> > > > bit, and if folks still feel (in a month or so?) that there are missing
>> > > > bits of information related to exfat, I'll be happy to go back and try
>> > > > to get them out as well.
>> >
>> > Hello Sasha!
>> >
>> > Now one month passed, so do you have some information when missing parts
>> > of documentation like TexFAT would be released to public?
>>
>> Sure, I'll see if I can get an approval to open it up.
>
>Ok!
>
>> Can I assume you will be implementing TexFAT support once the spec is
>> available?
>
>I cannot promise that I would implement something which I do not know
>how is working... It depends on how complicated TexFAT is and also how
>future exfat support in kernel would look like.
>
>But I'm interesting in implementing it.
Sure, thank you. This is more to let me tell my management that "there
is someone who wants to work on it" rather than committing you to do
that work :)
--
Thanks,
Sasha
On Wed, Oct 16, 2019 at 06:03:49PM +0200, Pali Roh?r wrote:
> > Can I assume you will be implementing TexFAT support once the spec is
> > available?
>
> I cannot promise that I would implement something which I do not know
> how is working... It depends on how complicated TexFAT is and also how
> future exfat support in kernel would look like.
>
> But I'm interesting in implementing it.
What devices need TexFAT? I thought it the old devices that used it are
long obsolete and gone. How is this feature going to be tested/used?
thanks,
greg k-h
On Wednesday 16 October 2019 09:22:11 Greg Kroah-Hartman wrote:
> On Wed, Oct 16, 2019 at 06:03:49PM +0200, Pali Rohár wrote:
> > > Can I assume you will be implementing TexFAT support once the spec is
> > > available?
> >
> > I cannot promise that I would implement something which I do not know
> > how is working... It depends on how complicated TexFAT is and also how
> > future exfat support in kernel would look like.
> >
> > But I'm interesting in implementing it.
>
> What devices need TexFAT? I thought it the old devices that used it are
> long obsolete and gone. How is this feature going to be tested/used?
Hi Greg! Per 3.1.16 of exFAT specification [1], TexFAT extension is the
only way how to use more FAT tables, like in FAT32 (where you normally
have two FATs). Secondary FAT table can be used e.g. for redundancy or
data recovery. For FAT32 volumes, e.g. fsck.fat uses secondary FAT table
when first one is corrupted.
Usage of just one FAT table in exFAT is just step backward from FAT32 as
secondary FAT table is sometimes the only way how to recover broken FAT
fs. So I do not think that exFAT is for old devices, but rather
non-exFAT is for old devices. Modern filesystems have journal or other
technique to do (fast|some) recovery, exFAT has nothing.
And how is this feature going to be used? That depends on specification.
[1] - https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification#3116-numberoffats-field
--
Pali Rohár
[email protected]
On Wed, Oct 16, 2019 at 06:32:31PM +0200, Pali Roh?r wrote:
> On Wednesday 16 October 2019 09:22:11 Greg Kroah-Hartman wrote:
> > On Wed, Oct 16, 2019 at 06:03:49PM +0200, Pali Roh?r wrote:
> > > > Can I assume you will be implementing TexFAT support once the spec is
> > > > available?
> > >
> > > I cannot promise that I would implement something which I do not know
> > > how is working... It depends on how complicated TexFAT is and also how
> > > future exfat support in kernel would look like.
> > >
> > > But I'm interesting in implementing it.
> >
> > What devices need TexFAT? I thought it the old devices that used it are
> > long obsolete and gone. How is this feature going to be tested/used?
>
> Hi Greg! Per 3.1.16 of exFAT specification [1], TexFAT extension is the
> only way how to use more FAT tables, like in FAT32 (where you normally
> have two FATs). Secondary FAT table can be used e.g. for redundancy or
> data recovery. For FAT32 volumes, e.g. fsck.fat uses secondary FAT table
> when first one is corrupted.
>
> Usage of just one FAT table in exFAT is just step backward from FAT32 as
> secondary FAT table is sometimes the only way how to recover broken FAT
> fs. So I do not think that exFAT is for old devices, but rather
> non-exFAT is for old devices. Modern filesystems have journal or other
> technique to do (fast|some) recovery, exFAT has nothing.
>
> And how is this feature going to be used? That depends on specification.
>
> [1] - https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification#3116-numberoffats-field
Ok, but given that the "only" os that can read/write the TexFAT
extension is WinCE, and that os is obsolete these days, it might be hard
to find images to test/validate against :)
But hey, I'll take the patch if you write it, no objection!
thanks,
greg k-h
On Wed, Oct 16, 2019 at 06:03:49PM +0200, Pali Roh?r wrote:
>On Wednesday 16 October 2019 10:31:13 Sasha Levin wrote:
>> On Wed, Oct 16, 2019 at 04:03:53PM +0200, Pali Roh?r wrote:
>> > On Friday 30 August 2019 09:56:47 Pali Roh?r wrote:
>> > > On Thursday 29 August 2019 19:35:06 Sasha Levin wrote:
>> > > > With regards to missing specs/docs/whatever - our main concern with this
>> > > > release was that we want full interoperability, which is why the spec
>> > > > was made public as-is without modifications from what was used
>> > > > internally. There's no "secret sauce" that Microsoft is hiding here.
>> > >
>> > > Ok, if it was just drop of "current version" of documentation then it
>> > > makes sense.
>> > >
>> > > > How about we give this spec/code time to get soaked and reviewed for a
>> > > > bit, and if folks still feel (in a month or so?) that there are missing
>> > > > bits of information related to exfat, I'll be happy to go back and try
>> > > > to get them out as well.
>> >
>> > Hello Sasha!
>> >
>> > Now one month passed, so do you have some information when missing parts
>> > of documentation like TexFAT would be released to public?
>>
>> Sure, I'll see if I can get an approval to open it up.
>
>Ok!
>
>> Can I assume you will be implementing TexFAT support once the spec is
>> available?
>
>I cannot promise that I would implement something which I do not know
>how is working... It depends on how complicated TexFAT is and also how
>future exfat support in kernel would look like.
>
>But I'm interesting in implementing it.
It looks like the reason this wasn't made public along with the exFAT
spec is that TexFAT is pretty much dead - it's old, there are no users
we are aware of, and digging it out of it's grave to make it public is
actually quite the headache.
Is this something you actually have a need for? an entity that has a
requirement for TexFAT? I'd would rather spend my time elsewhere than
digging TexFAT out of it's grave.
Is there anything else in the exFAT spec that is missing (and someone
actually wants/uses)?
--
Thanks,
Sasha
On Wed, 16 Oct 2019 16:33:17 -0400, Sasha Levin said:
> It looks like the reason this wasn't made public along with the exFAT
> spec is that TexFAT is pretty much dead - it's old, there are no users
> we are aware of, and digging it out of it's grave to make it public is
> actually quite the headache.
Ahh.. For some reason I had convinced myself that base exfat implementations
used at least the 'keep a backup FAT' part of TexFAT, because on a terabyte
external USB drive, losing the FAT would be painful. But if Windows 10 doesn't
do that either, then it's no great sin for Linux to not do it (and may cause
problems if Linux says "currently using FAT 2", and the disk is next used on a
Windows 10 box that only looks at FAT 1....)
On Wednesday 16 October 2019 16:33:17 Sasha Levin wrote:
> On Wed, Oct 16, 2019 at 06:03:49PM +0200, Pali Rohár wrote:
> > On Wednesday 16 October 2019 10:31:13 Sasha Levin wrote:
> > > On Wed, Oct 16, 2019 at 04:03:53PM +0200, Pali Rohár wrote:
> > > > On Friday 30 August 2019 09:56:47 Pali Rohár wrote:
> > > > > On Thursday 29 August 2019 19:35:06 Sasha Levin wrote:
> > > > > > With regards to missing specs/docs/whatever - our main concern with this
> > > > > > release was that we want full interoperability, which is why the spec
> > > > > > was made public as-is without modifications from what was used
> > > > > > internally. There's no "secret sauce" that Microsoft is hiding here.
> > > > >
> > > > > Ok, if it was just drop of "current version" of documentation then it
> > > > > makes sense.
> > > > >
> > > > > > How about we give this spec/code time to get soaked and reviewed for a
> > > > > > bit, and if folks still feel (in a month or so?) that there are missing
> > > > > > bits of information related to exfat, I'll be happy to go back and try
> > > > > > to get them out as well.
> > > >
> > > > Hello Sasha!
> > > >
> > > > Now one month passed, so do you have some information when missing parts
> > > > of documentation like TexFAT would be released to public?
> > >
> > > Sure, I'll see if I can get an approval to open it up.
> >
> > Ok!
> >
> > > Can I assume you will be implementing TexFAT support once the spec is
> > > available?
> >
> > I cannot promise that I would implement something which I do not know
> > how is working... It depends on how complicated TexFAT is and also how
> > future exfat support in kernel would look like.
> >
> > But I'm interesting in implementing it.
>
> It looks like the reason this wasn't made public along with the exFAT
> spec is that TexFAT is pretty much dead - it's old, there are no users
> we are aware of, and digging it out of it's grave to make it public is
> actually quite the headache.
>
> Is this something you actually have a need for? an entity that has a
> requirement for TexFAT?
Hi!
I do not have device with requirements for TexFAT. The first reason why
I wanted to use TexFAT was that it it the only way how to use more FAT
tables (e.g. secondary for backup) and information for that is missing
in released exFAT specification. This could bring better data recovery.
> I'd would rather spend my time elsewhere than digging TexFAT out of it's grave.
Ok.
I was hoping that it would be possible to easily use secondary FAT table
(from TexFAT extension) for redundancy without need to implement full
TexFAT, which could be also backward compatible with systems which do
not implement TexFAT extension at all. Similarly like using FAT32 disk
with two FAT tables is possible also on system which use first FAT
table.
> Is there anything else in the exFAT spec that is missing (and someone
> actually wants/uses)?
Currently I do not know.
--
Pali Rohár
[email protected]
On Wednesday 16 October 2019 17:53:43 Valdis Klētnieks wrote:
> and may cause problems if Linux says "currently using FAT 2", and the
> disk is next used on a Windows 10 box that only looks at FAT 1....
You should use same algorithm which is used for FAT32. Primary FAT is
first. And all operations are done on Secondary FAT and then is
Secondary FAT copied to Primary. This is backward compatible with
systems which operates only with first primary FAT. And other systems
which see both FATs can benefit from redundancy/recovery.
--
Pali Rohár
[email protected]
On Wednesday 28 August 2019 18:08:17 Greg Kroah-Hartman wrote:
> The full specification of the filesystem can be found at:
> https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification
FYI, it looks like that this released specification is just copy+paste
from exFAT patent https://patents.google.com/patent/US20150370821 which
was there publicly available for a while. And similar/same? copy was
available on the following site https://www.ntfs.com/exfat-overview.htm
--
Pali Rohár
[email protected]
Hello!
On Thursday 17 October 2019 09:50:08 Pali Rohár wrote:
> On Wednesday 16 October 2019 16:33:17 Sasha Levin wrote:
> > On Wed, Oct 16, 2019 at 06:03:49PM +0200, Pali Rohár wrote:
> > > On Wednesday 16 October 2019 10:31:13 Sasha Levin wrote:
> > > > On Wed, Oct 16, 2019 at 04:03:53PM +0200, Pali Rohár wrote:
> > > > > On Friday 30 August 2019 09:56:47 Pali Rohár wrote:
> > > > > > On Thursday 29 August 2019 19:35:06 Sasha Levin wrote:
> > > > > > > With regards to missing specs/docs/whatever - our main concern with this
> > > > > > > release was that we want full interoperability, which is why the spec
> > > > > > > was made public as-is without modifications from what was used
> > > > > > > internally. There's no "secret sauce" that Microsoft is hiding here.
> > > > > >
> > > > > > Ok, if it was just drop of "current version" of documentation then it
> > > > > > makes sense.
> > > > > >
> > > > > > > How about we give this spec/code time to get soaked and reviewed for a
> > > > > > > bit, and if folks still feel (in a month or so?) that there are missing
> > > > > > > bits of information related to exfat, I'll be happy to go back and try
> > > > > > > to get them out as well.
> > > > >
> > > > > Hello Sasha!
> > > > >
> > > > > Now one month passed, so do you have some information when missing parts
> > > > > of documentation like TexFAT would be released to public?
> > > >
> > > > Sure, I'll see if I can get an approval to open it up.
> > >
> > > Ok!
> > >
> > > > Can I assume you will be implementing TexFAT support once the spec is
> > > > available?
> > >
> > > I cannot promise that I would implement something which I do not know
> > > how is working... It depends on how complicated TexFAT is and also how
> > > future exfat support in kernel would look like.
> > >
> > > But I'm interesting in implementing it.
> >
> > It looks like the reason this wasn't made public along with the exFAT
> > spec is that TexFAT is pretty much dead - it's old, there are no users
> > we are aware of, and digging it out of it's grave to make it public is
> > actually quite the headache.
> >
> > Is this something you actually have a need for? an entity that has a
> > requirement for TexFAT?
>
> Hi!
>
> I do not have device with requirements for TexFAT. The first reason why
> I wanted to use TexFAT was that it it the only way how to use more FAT
> tables (e.g. secondary for backup) and information for that is missing
> in released exFAT specification. This could bring better data recovery.
>
> > I'd would rather spend my time elsewhere than digging TexFAT out of it's grave.
>
> Ok.
>
> I was hoping that it would be possible to easily use secondary FAT table
> (from TexFAT extension) for redundancy without need to implement full
> TexFAT, which could be also backward compatible with systems which do
> not implement TexFAT extension at all. Similarly like using FAT32 disk
> with two FAT tables is possible also on system which use first FAT
> table.
By the chance, is there any possibility to release TexFAT specification?
Usage of more FAT tables (even for Linux) could help with data recovery.
> > Is there anything else in the exFAT spec that is missing (and someone
> > actually wants/uses)?
>
> Currently I do not know.
I found one missing thing:
In released exFAT specification is not written how are Unicode code
points above U+FFFF represented in exFAT upcase table. Normally in
UTF-16 are Unicode code points above U+FFFF represented by surrogate
pairs but compression format of exFAT upcase table is not clear how to
do it there.
Are you able to send question about this problem to relevant MS people?
New Linux implementation of exfat which is waiting on mailing list just
do not support Unicode code points above U+FFFF in exFAT upcase table.
--
Pali Rohár
[email protected]
On Thu, Feb 13, 2020 at 01:06:56AM +0100, Pali Roh?r wrote:
>Hello!
>
>On Thursday 17 October 2019 09:50:08 Pali Roh?r wrote:
>> On Wednesday 16 October 2019 16:33:17 Sasha Levin wrote:
>> > On Wed, Oct 16, 2019 at 06:03:49PM +0200, Pali Roh?r wrote:
>> > > On Wednesday 16 October 2019 10:31:13 Sasha Levin wrote:
>> > > > On Wed, Oct 16, 2019 at 04:03:53PM +0200, Pali Roh?r wrote:
>> > > > > On Friday 30 August 2019 09:56:47 Pali Roh?r wrote:
>> > > > > > On Thursday 29 August 2019 19:35:06 Sasha Levin wrote:
>> > > > > > > With regards to missing specs/docs/whatever - our main concern with this
>> > > > > > > release was that we want full interoperability, which is why the spec
>> > > > > > > was made public as-is without modifications from what was used
>> > > > > > > internally. There's no "secret sauce" that Microsoft is hiding here.
>> > > > > >
>> > > > > > Ok, if it was just drop of "current version" of documentation then it
>> > > > > > makes sense.
>> > > > > >
>> > > > > > > How about we give this spec/code time to get soaked and reviewed for a
>> > > > > > > bit, and if folks still feel (in a month or so?) that there are missing
>> > > > > > > bits of information related to exfat, I'll be happy to go back and try
>> > > > > > > to get them out as well.
>> > > > >
>> > > > > Hello Sasha!
>> > > > >
>> > > > > Now one month passed, so do you have some information when missing parts
>> > > > > of documentation like TexFAT would be released to public?
>> > > >
>> > > > Sure, I'll see if I can get an approval to open it up.
>> > >
>> > > Ok!
>> > >
>> > > > Can I assume you will be implementing TexFAT support once the spec is
>> > > > available?
>> > >
>> > > I cannot promise that I would implement something which I do not know
>> > > how is working... It depends on how complicated TexFAT is and also how
>> > > future exfat support in kernel would look like.
>> > >
>> > > But I'm interesting in implementing it.
>> >
>> > It looks like the reason this wasn't made public along with the exFAT
>> > spec is that TexFAT is pretty much dead - it's old, there are no users
>> > we are aware of, and digging it out of it's grave to make it public is
>> > actually quite the headache.
>> >
>> > Is this something you actually have a need for? an entity that has a
>> > requirement for TexFAT?
>>
>> Hi!
>>
>> I do not have device with requirements for TexFAT. The first reason why
>> I wanted to use TexFAT was that it it the only way how to use more FAT
>> tables (e.g. secondary for backup) and information for that is missing
>> in released exFAT specification. This could bring better data recovery.
>>
>> > I'd would rather spend my time elsewhere than digging TexFAT out of it's grave.
>>
>> Ok.
>>
>> I was hoping that it would be possible to easily use secondary FAT table
>> (from TexFAT extension) for redundancy without need to implement full
>> TexFAT, which could be also backward compatible with systems which do
>> not implement TexFAT extension at all. Similarly like using FAT32 disk
>> with two FAT tables is possible also on system which use first FAT
>> table.
>
>By the chance, is there any possibility to release TexFAT specification?
>Usage of more FAT tables (even for Linux) could help with data recovery.
This would be a major pain in the arse to pull off (even more that
releasing exFAT itself) because TexFAT is effectively dead and no one
here cares about it. It's not even the case that there are devices which
are now left unsupported, the whole TexFAT scheme is just dead and gone.
Could I point you to the TexFAT patent instead
(https://patents.google.com/patent/US7613738B2/en)? It describes well
how TexFAT used to work.
>> > Is there anything else in the exFAT spec that is missing (and someone
>> > actually wants/uses)?
>>
>> Currently I do not know.
>
>I found one missing thing:
>
>In released exFAT specification is not written how are Unicode code
>points above U+FFFF represented in exFAT upcase table. Normally in
>UTF-16 are Unicode code points above U+FFFF represented by surrogate
>pairs but compression format of exFAT upcase table is not clear how to
>do it there.
>
>Are you able to send question about this problem to relevant MS people?
>
>New Linux implementation of exfat which is waiting on mailing list just
>do not support Unicode code points above U+FFFF in exFAT upcase table.
Sure, I'll forward this question on. I'll see if I can get someone from
their team who could be available to answer questions such as these in
the future - Microsoft is interested in maintaining compatiblity between
Linux and Windows exFAT implementations.
--
Thanks,
Sasha
On Thu, 13 Feb 2020 16:18:47 -0500, Sasha Levin said:
> >> I was hoping that it would be possible to easily use secondary FAT table
> >> (from TexFAT extension) for redundancy without need to implement full
> >> TexFAT, which could be also backward compatible with systems which do
> >> not implement TexFAT extension at all. Similarly like using FAT32 disk
> >> with two FAT tables is possible also on system which use first FAT
> >> table.
OK.. maybe I'm not sufficiently caffeinated, but how do you use 2 FAT tables on
a physical device and expect it to work properly on a system that uses just the
first FAT table, if the device is set to "use second table" when you mount it?
That sounds just too much like the failure modes of running fsck on a mounted
filesystem....
> >By the chance, is there any possibility to release TexFAT specification?
> >Usage of more FAT tables (even for Linux) could help with data recovery.
>
> This would be a major pain in the arse to pull off (even more that
> releasing exFAT itself) because TexFAT is effectively dead and no one
> here cares about it. It's not even the case that there are devices which
> are now left unsupported, the whole TexFAT scheme is just dead and gone.
>
> Could I point you to the TexFAT patent instead
> (https://patents.google.com/patent/US7613738B2/en)? It describes well
> how TexFAT used to work.
I don't think anybody wants the full TexFAT support - but having a backup copy
of the FAT would be nice in some circumstances.
Actually, that raises an question....
What the published spec says:
The File Allocation Table (FAT) region may contain up to two FATs, one in the
First FAT sub-region and another in the Second FAT sub-region. The NumberOfFats
field describes how many FATs this region contains. The valid values for the
NumberOfFats field are 1 and 2. Therefore, the First FAT sub-region always
contains a FAT. If the NumberOfFats field is two, then the Second FAT
sub-region also contains a FAT.
The ActiveFat field of the VolumeFlags field describes which FAT is active.
Only the VolumeFlags field in the Main Boot Sector is current. Implementations
shall treat the FAT which is not active as stale. Use of the inactive FAT and
switching between FATs is implementation specific.
Sasha: can you find out what the Windows implementation does regarding that
last sentence? Does it actively use both FAT sub-regions and switch between
them (probably not), or does it read the ActiveFAT value and use that one, or
does Windows just use NumberOfFats == 1?
I'm assuming that the fact the doc also says "NumberOfFats == 2 is only valid
for TexFAT volumes" possibly means "Microsoft thinks that's hardcoded at 1"
given the death of TexFAT. That would make adding alternate FAT support a
major challenge.
On the other hand, if Windows actually looks at the NumberOfFats value, finds
a 2, and ActiveFAT ==1 (meaning use second table) and says "OK, whatever" and
just uses the second table from there on out, it becomes a lot easier.
On Friday 14 February 2020 17:16:18 Valdis Klētnieks wrote:
> On Thu, 13 Feb 2020 16:18:47 -0500, Sasha Levin said:
>
> > >> I was hoping that it would be possible to easily use secondary FAT table
> > >> (from TexFAT extension) for redundancy without need to implement full
> > >> TexFAT, which could be also backward compatible with systems which do
> > >> not implement TexFAT extension at all. Similarly like using FAT32 disk
> > >> with two FAT tables is possible also on system which use first FAT
> > >> table.
>
> OK.. maybe I'm not sufficiently caffeinated, but how do you use 2 FAT tables on
> a physical device and expect it to work properly on a system that uses just the
> first FAT table, if the device is set to "use second table" when you mount it?
> That sounds just too much like the failure modes of running fsck on a mounted
> filesystem....
Idea is simple. Expects that we have a clean filesystem in correct
state. We load primary/active/main FAT table (just call it FAT1) and all
changes to filesystem would be done via second non-active FAT table
(FAT2). At unmount or sync or flush buffer times, FAT2 would be copied
back to the FAT1 and filesystem would be back in clean state.
So this should not break support for implementations which use just
FAT1. And if above implementation which use both FAT1 and FAT2 "crash"
during write operations to FAT2 it may be possible to reconstruct and
repair some parts of filesystem from FAT1 (as it would contain previous
state of some filesystem parts).
Via dirty bit can be detected if proper unmount occurred or not, and
fsck implementation could do use this fact and try to do repairing
(possible by asking user what should do).
Of course if implementation use only FAT1 we cannot use FAT2 for
repairing and therefore fsck should really ask user if it should use
FAT2 for repair or not.
If implementation use only FAT1, does not crash and let filesystem in
clean/correct state then there should not be any problem for
implementation which can use both FATs as it reads main state from FAT1.
Therefore these two implementations should be compatible and problem can
happen only if they let filesystem in inconsistent state. (But if they
let it in inconsistent state, then any implementation may have troubles
and fsck is needed).
I hope that it is more clear now...
> > >By the chance, is there any possibility to release TexFAT specification?
> > >Usage of more FAT tables (even for Linux) could help with data recovery.
> >
> > This would be a major pain in the arse to pull off (even more that
> > releasing exFAT itself) because TexFAT is effectively dead and no one
> > here cares about it. It's not even the case that there are devices which
> > are now left unsupported, the whole TexFAT scheme is just dead and gone.
> >
> > Could I point you to the TexFAT patent instead
> > (https://patents.google.com/patent/US7613738B2/en)? It describes well
> > how TexFAT used to work.
>
> I don't think anybody wants the full TexFAT support - but having a backup copy
> of the FAT would be nice in some circumstances.
Main problem is that we do not know what "full TexFAT support" means as
currently it is secret.
My original question for TexFAT was also because of NumberOfFats set to
2 is according to released exFAT specification possible only for TexFAT
volumes.
And from reading whole exFAT specification I see that better data
recovery can be achieved only by having backup copy of FAT table (and
allocation bitmap), which is limited to (currently undocumented) TexFAT
extension.
--
Pali Rohár
[email protected]
> Idea is simple. Expects that we have a clean filesystem in correct
> state. We load primary/active/main FAT table (just call it FAT1) and all
> changes to filesystem would be done via second non-active FAT table
> (FAT2). At unmount or sync or flush buffer times, FAT2 would be copied
> back to the FAT1 and filesystem would be back in clean state.
Somehow, scribbling on the non-active table for actual changes sounds like a
bad idea waiting to happen (partly because if you do that and crash, after the
reboot you remount, and it starts up with the now-stale FAT1 because you never
flagged that FAT as stale.
That means that if we started using the secondary FAT, we'd change the
ActiveFAT variable to indicate that. And if we do that, we need to also set
num_fat to 2 because num_fat 1 and ActiveFAT pointing at the second FAT is
*definitely* bogus.
And that could result in us crashing and leaving the device with a header that
says 'num_fat == 2', ActiveFAT == second, and the dirty bit set - and the user
next uses the filesystem on a system/device that hard-codes that there's only 1 FAT,
so it blindly goes on its merry way using a FAT that's stale and never realizing it.
And that's actually the same failure mode as in the first paragraph - you start
off using FAT1 because you don't see an indication that it's stale.
On Thursday 13 February 2020 16:18:47 Sasha Levin wrote:
> On Thu, Feb 13, 2020 at 01:06:56AM +0100, Pali Rohár wrote:
> > In released exFAT specification is not written how are Unicode code
> > points above U+FFFF represented in exFAT upcase table. Normally in
> > UTF-16 are Unicode code points above U+FFFF represented by surrogate
> > pairs but compression format of exFAT upcase table is not clear how to
> > do it there.
> >
> > Are you able to send question about this problem to relevant MS people?
> >
> > New Linux implementation of exfat which is waiting on mailing list just
> > do not support Unicode code points above U+FFFF in exFAT upcase table.
>
> Sure, I'll forward this question on. I'll see if I can get someone from
> their team who could be available to answer questions such as these in
> the future - Microsoft is interested in maintaining compatiblity between
> Linux and Windows exFAT implementations.
Hello Sasha! Have you got any answer from exfat MS team about upcase
table for Unicode code points above U+FFFF?
On Tue, Apr 21, 2020 at 11:30:45PM +0200, Pali Roh?r wrote:
>On Thursday 13 February 2020 16:18:47 Sasha Levin wrote:
>> On Thu, Feb 13, 2020 at 01:06:56AM +0100, Pali Roh?r wrote:
>> > In released exFAT specification is not written how are Unicode code
>> > points above U+FFFF represented in exFAT upcase table. Normally in
>> > UTF-16 are Unicode code points above U+FFFF represented by surrogate
>> > pairs but compression format of exFAT upcase table is not clear how to
>> > do it there.
>> >
>> > Are you able to send question about this problem to relevant MS people?
>> >
>> > New Linux implementation of exfat which is waiting on mailing list just
>> > do not support Unicode code points above U+FFFF in exFAT upcase table.
>>
>> Sure, I'll forward this question on. I'll see if I can get someone from
>> their team who could be available to answer questions such as these in
>> the future - Microsoft is interested in maintaining compatiblity between
>> Linux and Windows exFAT implementations.
>
>Hello Sasha! Have you got any answer from exfat MS team about upcase
>table for Unicode code points above U+FFFF?
Sorry for taking so long. This is my understanding from the Windows
folks: Windows filesystems just don't support variable encoding length,
and expect UCS-2 strings.
--
Thanks,
Sasha
On Monday 27 April 2020 11:49:13 Sasha Levin wrote:
> On Tue, Apr 21, 2020 at 11:30:45PM +0200, Pali Rohár wrote:
> > On Thursday 13 February 2020 16:18:47 Sasha Levin wrote:
> > > On Thu, Feb 13, 2020 at 01:06:56AM +0100, Pali Rohár wrote:
> > > > In released exFAT specification is not written how are Unicode code
> > > > points above U+FFFF represented in exFAT upcase table. Normally in
> > > > UTF-16 are Unicode code points above U+FFFF represented by surrogate
> > > > pairs but compression format of exFAT upcase table is not clear how to
> > > > do it there.
> > > >
> > > > Are you able to send question about this problem to relevant MS people?
> > > >
> > > > New Linux implementation of exfat which is waiting on mailing list just
> > > > do not support Unicode code points above U+FFFF in exFAT upcase table.
> > >
> > > Sure, I'll forward this question on. I'll see if I can get someone from
> > > their team who could be available to answer questions such as these in
> > > the future - Microsoft is interested in maintaining compatiblity between
> > > Linux and Windows exFAT implementations.
> >
> > Hello Sasha! Have you got any answer from exfat MS team about upcase
> > table for Unicode code points above U+FFFF?
>
> Sorry for taking so long. This is my understanding from the Windows
> folks: Windows filesystems just don't support variable encoding length,
> and expect UCS-2 strings.
Ok, so should I understand your answer as exFAT upcase table does not
support representing Unicode code points above U+FFFF and therefore
exFAT implementation should expect that toupper(u) = u and tolower(u) = u
for any Unicode code point u in range [U+10000, U+10FFFF]? This is how
current exfat linux driver behave.