Commit c48c9f7f authored by Valdis Klētnieks's avatar Valdis Klētnieks Committed by Greg Kroah-Hartman

staging: exfat: add exfat filesystem code to staging

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-specificationSigned-off-by: default avatarValdis Kletnieks <valdis.kletnieks@vt.edu>
Signed-off-by: default avatarSasha Levin <alexander.levin@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Link: https://lore.kernel.org/r/20190828160817.6250-1-gregkh@linuxfoundation.orgSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 3982f1df
......@@ -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 <valdis.kletnieks@vt.edu>
S: Maintained
F: fs/exfat/
EXT2 FILE SYSTEM
M: Jan Kara <jack@suse.com>
L: linux-ext4@vger.kernel.org
......
......@@ -118,4 +118,6 @@ source "drivers/staging/kpc2000/Kconfig"
source "drivers/staging/isdn/Kconfig"
source "drivers/staging/exfat/Kconfig"
endif # STAGING
......@@ -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/
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.
# 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
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...
/* 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 */
// 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);
}
// 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);
}
// 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,
&sector);
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,
&sector);
if (!file_ep)
return FFS_MEDIAERR;
strm_ep = (struct strm_dentry_t *)get_entry_in_dir(sb, p_dir, entry+1,
&sector);
if (!strm_ep)
return FFS_MEDIAERR;
init_file_entry(file_ep, type);
buf_modify(sb, sector);
init_strm_entry(strm_ep, flags, start_clu, size);
buf_modify(sb, sector);
return FFS_SUCCESS;
}
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,
&sector);
if (!dos_ep)
return FFS_MEDIAERR;
dos_ep->lcase = p_dosname->name_case;
memcpy(dos_ep->name, p_dosname->name, DOS_NAME_LENGTH);
buf_modify(sb, sector);
if ((--num_entries) > 0) {
chksum = calc_checksum_1byte((void *)dos_ep->name,
DOS_NAME_LENGTH, 0);
for (i = 1; i < num_entries; i++) {
ext_ep = (struct ext_dentry_t *)get_entry_in_dir(sb,
p_dir,
entry - i,
&sector);
if (!ext_ep)
return FFS_MEDIAERR;
init_ext_entry(ext_ep, i, chksum, uniname);
buf_modify(sb, sector);
uniname += 13;
}
ext_ep = (struct ext_dentry_t *)get_entry_in_dir(sb, p_dir,
entry - i,
&sector);
if (!ext_ep)
return FFS_MEDIAERR;
init_ext_entry(ext_ep, i+0x40, chksum, uniname);
buf_modify(sb, sector);
}
return FFS_SUCCESS;
}
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,
&sector);
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,
&sector);
if (!strm_ep)
return FFS_MEDIAERR;
strm_ep->name_len = p_uniname->name_len;
SET16_A(strm_ep->name_hash, p_uniname->name_hash);
buf_modify(sb, sector);
for (i = 2; i < num_entries; i++) {
name_ep = (struct name_dentry_t *)get_entry_in_dir(sb, p_dir,
entry + i,
&sector);
if (!name_ep)
return FFS_MEDIAERR;
init_name_entry(name_ep, uniname);
buf_modify(sb, sector);
uniname += 15;
}
update_dir_checksum(sb, p_dir, entry);
return FFS_SUCCESS;
}
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, &sector);
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, &sector);
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,
&sector);
if (!file_ep)
return;
buf_lock(sb, sector);
num_entries = (s32) file_ep->num_ext + 1;
chksum = calc_checksum_2byte((void *)file_ep, DENTRY_SIZE, 0,
CS_DIR_ENTRY);
for (i = 1; i < num_entries; i++) {
ep = get_entry_in_dir(sb, p_dir, entry+i, NULL);
if (!ep) {
buf_unlock(sb, sector);
return;
}
chksum = calc_checksum_2byte((void *)ep, DENTRY_SIZE, chksum,
CS_DEFAULT);
}
SET16_A(file_ep->checksum, chksum);
buf_modify(sb, sector);
buf_unlock(sb, sector);
}
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, &sector);
if (!ep)
return -1;
p_fs->fs_func->set_entry_size(ep, size);
p_fs->fs_func->set_entry_flag(ep, p_dir->flags);
buf_modify(sb, sector);
update_dir_checksum(sb, &(fid->dir),
fid->entry);
}
}
i_size_write(inode, i_size_read(inode)+p_fs->cluster_size);
EXFAT_I(inode)->mmu_private += p_fs->cluster_size;
EXFAT_I(inode)->fid.size += p_fs->cluster_size;
EXFAT_I(inode)->fid.flags = p_dir->flags;
inode->i_blocks += 1 << (p_fs->cluster_size_bits - 9);
}
return dentry;
}
/* 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, &sector);
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, &sector_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, &sector_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,
&sector_old);
buf_lock(sb, sector_old);
epnew = get_entry_in_dir(sb, p_dir, newentry + 1,
&sector_new);
if (!epold || !epnew) {
buf_unlock(sb, sector_old);
return FFS_MEDIAERR;
}
memcpy((void *)epnew, (void *)epold, DENTRY_SIZE);
buf_modify(sb, sector_new);
buf_unlock(sb, sector_old);
}
ret = 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, &sector_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, &sector_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,
&sector_mov);
buf_lock(sb, sector_mov);
epnew = get_entry_in_dir(sb, p_newdir, newentry + 1,
&sector_new);
if (!epmov || !epnew) {
buf_unlock(sb, sector_mov);
return FFS_MEDIAERR;
}
memcpy((void *)epnew, (void *)epmov, DENTRY_SIZE);
buf_modify(sb, sector_new);
buf_unlock(sb, sector_mov);
} else if (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, &sector_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;
}
// 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;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
// 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
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment