fs: Add sdfat

* Samsung package version: G950FXXU1CRAP (yes, really)

Change-Id: Id866574b34d4434fd4955fac154c9684210abebd
Signed-off-by: Paul Keith <javelinanddart@gmail.com>
This commit is contained in:
Paul Keith 2018-02-19 17:31:10 +01:00 committed by Francescodario Cuzzocrea
parent 4aedbbf079
commit 72c5a7ae3e
29 changed files with 21545 additions and 0 deletions

View File

@ -114,6 +114,7 @@ menu "DOS/FAT/NT Filesystems"
source "fs/fat/Kconfig"
source "fs/exfat/Kconfig"
source "fs/ntfs/Kconfig"
source "fs/sdfat/Kconfig"
endmenu
endif # BLOCK

View File

@ -80,6 +80,7 @@ obj-$(CONFIG_CODA_FS) += coda/
obj-$(CONFIG_MINIX_FS) += minix/
obj-$(CONFIG_FAT_FS) += fat/
obj-$(CONFIG_EXFAT_FS) += exfat/
obj-$(CONFIG_SDFAT_FS) += sdfat/
obj-$(CONFIG_BFS_FS) += bfs/
obj-$(CONFIG_ISO9660_FS) += isofs/
obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+

103
fs/sdfat/Kconfig Normal file
View File

@ -0,0 +1,103 @@
config SDFAT_FS
tristate
default y
select NLS
select NLS_UTF8
select NLS_CODEPAGE_437
select NLS_ISO8859_1
help
If you want to use the sdFAT file system, then you must say Y or M
here to inlucde sdFAT support.
sdFAT is unified FAT-based file system which supports not only fat12/
16/32 with vfat but also exfat. sdFAT supports winnt short-name rule.
(winnt: emulate the Windows NT rule for display/create.)
To compile this as a module, choose M here: the module will be called
sdfat_core and sdfat_fs.
config SDFAT_DELAYED_META_DIRTY
bool "Enable delayed metadata dirty"
default y
depends on SDFAT_FS
help
If you enable this feature, metadata(FAT/Directory entry) is updated
by flush thread.
config SDFAT_SUPPORT_DIR_SYNC
bool "Enable supporting dir sync"
default n
depends on SDFAT_FS
help
If you enable this feature, the modification for directory operation
is written to a storage at once.
config SDFAT_DEFAULT_CODEPAGE
int "Default codepage for sdFAT"
default 437
depends on SDFAT_FS
help
This option should be set to the codepage of your sdFAT filesystems.
config SDFAT_DEFAULT_IOCHARSET
string "Default iocharset for sdFAT"
default "utf8"
depends on SDFAT_FS
help
Set this to the default input/output character set you'd
like sdFAT to use. It should probably match the character set
that most of your sdFAT filesystems use, and can be overridden
with the "iocharset" mount option for sdFAT filesystems.
config SDFAT_CHECK_RO_ATTR
bool "Check read-only attribute"
default n
depends on SDFAT_FS
config SDFAT_ALIGNED_MPAGE_WRITE
bool "Enable supporting aligned mpage_write"
default y
depends on SDFAT_FS
config SDFAT_VIRTUAL_XATTR
bool "Virtual xattr support for sdFAT"
default y
depends on SDFAT_FS
help
To support virtual xattr.
config SDFAT_VIRTUAL_XATTR_SELINUX_LABEL
string "Default string for SELinux label"
default "u:object_r:sdcard_external:s0"
depends on SDFAT_FS && SDFAT_VIRTUAL_XATTR
help
Set this to the default string for SELinux label.
config SDFAT_SUPPORT_STLOG
bool "Enable storage log"
default y
depends on SDFAT_FS && PROC_STLOG
config SDFAT_DEBUG
bool "enable debug features"
depends on SDFAT_FS
default y
config SDFAT_DBG_IOCTL
bool "enable debug-ioctl features"
depends on SDFAT_FS && SDFAT_DEBUG
default n
config SDFAT_DBG_MSG
bool "enable debug messages"
depends on SDFAT_FS && SDFAT_DEBUG
default y
config SDFAT_DBG_BUGON
bool "enable strict BUG_ON() for debugging"
depends on SDFAT_FS && SDFAT_DEBUG
default n
config SDFAT_STATISTICS
bool "enable statistics for bigdata"
depends on SDFAT_FS
default y

24
fs/sdfat/Makefile Normal file
View File

@ -0,0 +1,24 @@
#
# Makefile for the linux FAT12/16/32(VFAT)/64(exFAT) filesystem driver.
#
obj-$(CONFIG_SDFAT_FS) += sdfat_fs.o
sdfat_fs-objs := sdfat.o core.o core_fat.o core_exfat.o api.o blkdev.o \
fatent.o amap_smart.o cache.o dfr.o nls.o misc.o \
mpage.o extent.o
sdfat_fs-$(CONFIG_SDFAT_VIRTUAL_XATTR) += xattr.o
sdfat_fs-$(CONFIG_SDFAT_STATISTICS) += statistics.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
cscope:
rm -rf cscope.files cscope.files
find $(PWD) \( -name '*.c' -o -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.s' -o -name '*.S' \) -print > cscope.files
cscope

1302
fs/sdfat/amap_smart.c Normal file

File diff suppressed because it is too large Load Diff

137
fs/sdfat/amap_smart.h Normal file
View File

@ -0,0 +1,137 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SDFAT_AMAP_H
#define _SDFAT_AMAP_H
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/rbtree.h>
/* AMAP Configuration Variable */
#define SMART_ALLOC_N_HOT_AU (5)
/* Allocating Destination (for smart allocator):
* moved to sdfat.h
*/
/*
* #define ALLOC_COLD_ALIGNED (1)
* #define ALLOC_COLD_PACKING (2)
* #define ALLOC_COLD_SEQ (4)
*/
/* Minimum sectors for support AMAP create */
#define AMAP_MIN_SUPPORT_SECTORS (1048576)
#define amap_add_hot_au(amap, au) amap_insert_to_list(au, &amap->slist_hot)
/* singly linked list */
struct slist_head {
struct slist_head *next;
struct slist_head *head;
};
/* AU entry type */
typedef struct __AU_INFO_T {
uint16_t idx; /* the index of the AU (0, 1, 2, ... ) */
uint16_t free_clusters; /* # of available cluster */
union {
struct list_head head;
struct slist_head shead;/* singly linked list head for hot list */
};
} AU_INFO_T;
/* Allocation Target AU */
typedef struct __TARGET_AU_T {
AU_INFO_T *au; /* Working AU */
uint16_t idx; /* Intra-AU cluster index */
uint16_t clu_to_skip; /* Clusters to skip */
} TARGET_AU_T;
/* AMAP free-clusters-based node */
typedef struct {
struct list_head head; /* the list of AUs */
} FCLU_NODE_T;
/* AMAP options */
typedef struct {
unsigned int packing_ratio; /* Tunable packing ratio */
unsigned int au_size; /* AU size in sectors */
unsigned int au_align_factor; /* Hidden sectors % au_size */
} AMAP_OPT_T;
typedef struct __AMAP_T {
spinlock_t amap_lock; /* obsolete */
struct super_block *sb;
int n_au;
int n_clean_au, n_full_au;
int clu_align_bias;
uint16_t clusters_per_au;
AU_INFO_T **au_table; /* An array of AU_INFO entries */
AMAP_OPT_T option;
/* Size-based AU management pool (cold) */
FCLU_NODE_T *fclu_nodes; /* An array of listheads */
int fclu_order; /* Page order that fclu_nodes needs */
int fclu_hint; /* maximum # of free clusters in an AU */
/* Hot AU list */
int total_fclu_hot; /* Free clusters in hot list */
struct slist_head slist_hot; /* Hot AU list */
/* Ignored AU list */
struct slist_head slist_ignored;
/* Allocator variables (keep 2 AUs at maximum) */
TARGET_AU_T cur_cold;
TARGET_AU_T cur_hot;
int n_need_packing;
} AMAP_T;
/* AU table */
#define N_AU_PER_TABLE (int)(PAGE_SIZE / sizeof(AU_INFO_T))
#define GET_AU(amap, i_AU) (amap->au_table[(i_AU) / N_AU_PER_TABLE] + ((i_AU) % N_AU_PER_TABLE))
//#define MAX_CLU_PER_AU (int)(PAGE_SIZE / sizeof(FCLU_NODE_T))
#define MAX_CLU_PER_AU (1024)
/* Cold AU bucket <-> # of freeclusters */
#define NODE_CLEAN(amap) (&amap->fclu_nodes[amap->clusters_per_au - 1])
#define NODE(fclu, amap) (&amap->fclu_nodes[fclu - 1])
#define FREE_CLUSTERS(node, amap) ((int)(node - amap->fclu_nodes) + 1)
/* AU status */
#define MAGIC_WORKING ((struct slist_head *)0xFFFF5091)
#define IS_AU_HOT(au, amap) (au->shead.head == &amap->slist_hot)
#define IS_AU_IGNORED(au, amap) (au->shead.head == &amap->slist_ignored)
#define IS_AU_WORKING(au, amap) (au->shead.head == MAGIC_WORKING)
#define SET_AU_WORKING(au) (au->shead.head = MAGIC_WORKING)
/* AU <-> cluster */
#define i_AU_of_CLU(amap, clu) ((amap->clu_align_bias + clu) / amap->clusters_per_au)
#define CLU_of_i_AU(amap, i_au, idx) \
((uint32_t)(i_au) * (uint32_t)amap->clusters_per_au + (idx) - amap->clu_align_bias)
/*
* NOTE : AMAP internal functions are moved to core.h
*/
#endif /* _SDFAT_AMAP_H */

629
fs/sdfat/api.c Normal file
View File

@ -0,0 +1,629 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/************************************************************************/
/* */
/* PROJECT : exFAT & FAT12/16/32 File System */
/* FILE : sdfat_api.c */
/* PURPOSE : sdFAT volume lock layer */
/* */
/************************************************************************/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include "version.h"
#include "config.h"
#include "sdfat.h"
#include "core.h"
/*----------------------------------------------------------------------*/
/* Internal structures */
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* Constant & Macro Definitions */
/*----------------------------------------------------------------------*/
static DEFINE_MUTEX(_lock_core);
/*----------------------------------------------------------------------*/
/* Global Variable Definitions */
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* Local Variable Definitions */
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* Local Function Declarations */
/*----------------------------------------------------------------------*/
/*======================================================================*/
/* Global Function Definitions */
/* - All functions for global use have same return value format, */
/* that is, 0 on success and minus error number on */
/* various error condition. */
/*======================================================================*/
/*----------------------------------------------------------------------*/
/* sdFAT Filesystem Init & Exit Functions */
/*----------------------------------------------------------------------*/
s32 fsapi_init(void)
{
return fscore_init();
}
s32 fsapi_shutdown(void)
{
return fscore_shutdown();
}
/*----------------------------------------------------------------------*/
/* Volume Management Functions */
/*----------------------------------------------------------------------*/
/* mount the file system volume */
s32 fsapi_mount(struct super_block *sb)
{
s32 err;
/* acquire the core lock for file system ccritical section */
mutex_lock(&_lock_core);
err = meta_cache_init(sb);
if (err)
goto out;
err = fscore_mount(sb);
out:
if (err)
meta_cache_shutdown(sb);
/* release the core lock for file system critical section */
mutex_unlock(&_lock_core);
return err;
}
EXPORT_SYMBOL(fsapi_mount);
/* unmount the file system volume */
s32 fsapi_umount(struct super_block *sb)
{
s32 err;
/* acquire the core lock for file system ccritical section */
mutex_lock(&_lock_core);
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_umount(sb);
meta_cache_shutdown(sb);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
/* release the core lock for file system critical section */
mutex_unlock(&_lock_core);
return err;
}
EXPORT_SYMBOL(fsapi_umount);
/* get the information of a file system volume */
s32 fsapi_statfs(struct super_block *sb, VOL_INFO_T *info)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
/* check the validity of pointer parameters */
ASSERT(info);
if (fsi->used_clusters == (u32) ~0) {
s32 err;
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_statfs(sb, info);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
info->FatType = fsi->vol_type;
info->ClusterSize = fsi->cluster_size;
info->NumClusters = fsi->num_clusters - 2; /* clu 0 & 1 */
info->UsedClusters = fsi->used_clusters + fsi->reserved_clusters;
info->FreeClusters = info->NumClusters - info->UsedClusters;
return 0;
}
EXPORT_SYMBOL(fsapi_statfs);
/* synchronize a file system volume */
s32 fsapi_sync_fs(struct super_block *sb, s32 do_sync)
{
s32 err;
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_sync_fs(sb, do_sync);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_sync_fs);
s32 fsapi_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync)
{
s32 err;
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_set_vol_flags(sb, new_flag, always_sync);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_set_vol_flags);
/*----------------------------------------------------------------------*/
/* File Operation Functions */
/*----------------------------------------------------------------------*/
/* lookup */
s32 fsapi_lookup(struct inode *inode, u8 *path, FILE_ID_T *fid)
{
s32 err;
struct super_block *sb = inode->i_sb;
/* check the validity of pointer parameters */
ASSERT(fid && path);
if (unlikely(!strlen(path)))
return -EINVAL;
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_lookup(inode, path, fid);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_lookup);
/* create a file */
s32 fsapi_create(struct inode *inode, u8 *path, u8 mode, FILE_ID_T *fid)
{
s32 err;
struct super_block *sb = inode->i_sb;
/* check the validity of pointer parameters */
ASSERT(fid && path);
if (unlikely(!strlen(path)))
return -EINVAL;
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_create(inode, path, mode, fid);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_create);
/* read the target string of symlink */
s32 fsapi_read_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount)
{
s32 err;
struct super_block *sb = inode->i_sb;
/* check the validity of pointer parameters */
ASSERT(fid && buffer);
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_read_link(inode, fid, buffer, count, rcount);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_read_link);
/* write the target string of symlink */
s32 fsapi_write_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount)
{
s32 err;
struct super_block *sb = inode->i_sb;
/* check the validity of pointer parameters */
ASSERT(fid && buffer);
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_write_link(inode, fid, buffer, count, wcount);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_write_link);
/* resize the file length */
s32 fsapi_truncate(struct inode *inode, u64 old_size, u64 new_size)
{
s32 err;
struct super_block *sb = inode->i_sb;
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
TMSG("%s entered (inode %p size %llu)\n", __func__, inode, new_size);
err = fscore_truncate(inode, old_size, new_size);
TMSG("%s exitted (%d)\n", __func__, err);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_truncate);
/* rename or move a old file into a new file */
s32 fsapi_rename(struct inode *old_parent_inode, FILE_ID_T *fid,
struct inode *new_parent_inode, struct dentry *new_dentry)
{
s32 err;
struct super_block *sb = old_parent_inode->i_sb;
/* check the validity of pointer parameters */
ASSERT(fid);
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_rename(old_parent_inode, fid, new_parent_inode, new_dentry);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_rename);
/* remove a file */
s32 fsapi_remove(struct inode *inode, FILE_ID_T *fid)
{
s32 err;
struct super_block *sb = inode->i_sb;
/* check the validity of pointer parameters */
ASSERT(fid);
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_remove(inode, fid);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_remove);
/* get the information of a given file */
s32 fsapi_read_inode(struct inode *inode, DIR_ENTRY_T *info)
{
s32 err;
struct super_block *sb = inode->i_sb;
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
TMSG("%s entered (inode %p info %p\n", __func__, inode, info);
err = fscore_read_inode(inode, info);
TMSG("%s exited (err:%d)\n", __func__, err);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_read_inode);
/* set the information of a given file */
s32 fsapi_write_inode(struct inode *inode, DIR_ENTRY_T *info, int sync)
{
s32 err;
struct super_block *sb = inode->i_sb;
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
TMSG("%s entered (inode %p info %p sync:%d\n",
__func__, inode, info, sync);
err = fscore_write_inode(inode, info, sync);
TMSG("%s exited (err:%d)\n", __func__, err);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_write_inode);
/* return the cluster number in the given cluster offset */
s32 fsapi_map_clus(struct inode *inode, s32 clu_offset, u32 *clu, int dest)
{
s32 err;
struct super_block *sb = inode->i_sb;
/* check the validity of pointer parameters */
ASSERT(clu);
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
TMSG("%s entered (inode:%p clus:%08x dest:%d\n",
__func__, inode, *clu, dest);
err = fscore_map_clus(inode, clu_offset, clu, dest);
TMSG("%s exited (clu:%08x err:%d)\n", __func__, *clu, err);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_map_clus);
/* reserve a cluster */
s32 fsapi_reserve_clus(struct inode *inode)
{
s32 err;
struct super_block *sb = inode->i_sb;
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
TMSG("%s entered (inode:%p)\n", __func__, inode);
err = fscore_reserve_clus(inode);
TMSG("%s exited (err:%d)\n", __func__, err);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_reserve_clus);
/*----------------------------------------------------------------------*/
/* Directory Operation Functions */
/*----------------------------------------------------------------------*/
/* create(make) a directory */
s32 fsapi_mkdir(struct inode *inode, u8 *path, FILE_ID_T *fid)
{
s32 err;
struct super_block *sb = inode->i_sb;
/* check the validity of pointer parameters */
ASSERT(fid && path);
if (unlikely(!strlen(path)))
return -EINVAL;
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_mkdir(inode, path, fid);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_mkdir);
/* read a directory entry from the opened directory */
s32 fsapi_readdir(struct inode *inode, DIR_ENTRY_T *dir_entry)
{
s32 err;
struct super_block *sb = inode->i_sb;
/* check the validity of pointer parameters */
ASSERT(dir_entry);
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_readdir(inode, dir_entry);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_readdir);
/* remove a directory */
s32 fsapi_rmdir(struct inode *inode, FILE_ID_T *fid)
{
s32 err;
struct super_block *sb = inode->i_sb;
/* check the validity of pointer parameters */
ASSERT(fid);
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_rmdir(inode, fid);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_rmdir);
/* unlink a file.
* that is, remove an entry from a directory. BUT don't truncate
*/
s32 fsapi_unlink(struct inode *inode, FILE_ID_T *fid)
{
s32 err;
struct super_block *sb = inode->i_sb;
/* check the validity of pointer parameters */
ASSERT(fid);
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = fscore_unlink(inode, fid);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_unlink);
/* reflect the internal dirty flags to VFS bh dirty flags */
s32 fsapi_cache_flush(struct super_block *sb, int do_sync)
{
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
fcache_flush(sb, do_sync);
dcache_flush(sb, do_sync);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return 0;
}
EXPORT_SYMBOL(fsapi_cache_flush);
/* release FAT & buf cache */
s32 fsapi_cache_release(struct super_block *sb)
{
#ifdef CONFIG_SDFAT_DEBUG
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
fcache_release_all(sb);
dcache_release_all(sb);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
#endif /* CONFIG_SDFAT_DEBUG */
return 0;
}
EXPORT_SYMBOL(fsapi_cache_release);
u32 fsapi_get_au_stat(struct super_block *sb, s32 mode)
{
/* volume lock is not required */
return fscore_get_au_stat(sb, mode);
}
EXPORT_SYMBOL(fsapi_get_au_stat);
/* clear extent cache */
void fsapi_invalidate_extent(struct inode *inode)
{
/* Volume lock is not required,
* because it is only called by evict_inode.
* If any other function can call it,
* you should check whether volume lock is needed or not.
*/
extent_cache_inval_inode(inode);
}
EXPORT_SYMBOL(fsapi_invalidate_extent);
#ifdef CONFIG_SDFAT_DFR
/*----------------------------------------------------------------------*/
/* Defragmentation related */
/*----------------------------------------------------------------------*/
s32 fsapi_dfr_get_info(struct super_block *sb, void *arg)
{
/* volume lock is not required */
return defrag_get_info(sb, (struct defrag_info_arg *)arg);
}
EXPORT_SYMBOL(fsapi_dfr_get_info);
s32 fsapi_dfr_scan_dir(struct super_block *sb, void *args)
{
s32 err;
/* check the validity of pointer parameters */
ASSERT(args);
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = defrag_scan_dir(sb, (struct defrag_trav_arg *)args);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_dfr_scan_dir);
s32 fsapi_dfr_validate_clus(struct inode *inode, void *chunk, int skip_prev)
{
s32 err;
struct super_block *sb = inode->i_sb;
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = defrag_validate_cluster(inode,
(struct defrag_chunk_info *)chunk, skip_prev);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_dfr_validate_clus);
s32 fsapi_dfr_reserve_clus(struct super_block *sb, s32 nr_clus)
{
s32 err;
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = defrag_reserve_clusters(sb, nr_clus);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_dfr_reserve_clus);
s32 fsapi_dfr_mark_ignore(struct super_block *sb, unsigned int clus)
{
/* volume lock is not required */
return defrag_mark_ignore(sb, clus);
}
EXPORT_SYMBOL(fsapi_dfr_mark_ignore);
void fsapi_dfr_unmark_ignore_all(struct super_block *sb)
{
/* volume lock is not required */
defrag_unmark_ignore_all(sb);
}
EXPORT_SYMBOL(fsapi_dfr_unmark_ignore_all);
s32 fsapi_dfr_map_clus(struct inode *inode, u32 clu_offset, u32 *clu)
{
s32 err;
struct super_block *sb = inode->i_sb;
/* check the validity of pointer parameters */
ASSERT(clu);
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
err = defrag_map_cluster(inode, clu_offset, clu);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
return err;
}
EXPORT_SYMBOL(fsapi_dfr_map_clus);
void fsapi_dfr_writepage_endio(struct page *page)
{
/* volume lock is not required */
defrag_writepage_end_io(page);
}
EXPORT_SYMBOL(fsapi_dfr_writepage_endio);
void fsapi_dfr_update_fat_prev(struct super_block *sb, int force)
{
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
defrag_update_fat_prev(sb, force);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
}
EXPORT_SYMBOL(fsapi_dfr_update_fat_prev);
void fsapi_dfr_update_fat_next(struct super_block *sb)
{
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
defrag_update_fat_next(sb);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
}
EXPORT_SYMBOL(fsapi_dfr_update_fat_next);
void fsapi_dfr_check_discard(struct super_block *sb)
{
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
defrag_check_discard(sb);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
}
EXPORT_SYMBOL(fsapi_dfr_check_discard);
void fsapi_dfr_free_clus(struct super_block *sb, u32 clus)
{
mutex_lock(&(SDFAT_SB(sb)->s_vlock));
defrag_free_cluster(sb, clus);
mutex_unlock(&(SDFAT_SB(sb)->s_vlock));
}
EXPORT_SYMBOL(fsapi_dfr_free_clus);
s32 fsapi_dfr_check_dfr_required(struct super_block *sb, int *totalau, int *cleanau, int *fullau)
{
/* volume lock is not required */
return defrag_check_defrag_required(sb, totalau, cleanau, fullau);
}
EXPORT_SYMBOL(fsapi_dfr_check_dfr_required);
s32 fsapi_dfr_check_dfr_on(struct inode *inode, loff_t start, loff_t end, s32 cancel, const char *caller)
{
/* volume lock is not required */
return defrag_check_defrag_on(inode, start, end, cancel, caller);
}
EXPORT_SYMBOL(fsapi_dfr_check_dfr_on);
#ifdef CONFIG_SDFAT_DFR_DEBUG
void fsapi_dfr_spo_test(struct super_block *sb, int flag, const char *caller)
{
/* volume lock is not required */
defrag_spo_test(sb, flag, caller);
}
EXPORT_SYMBOL(fsapi_dfr_spo_test);
#endif
#endif /* CONFIG_SDFAT_DFR */
/* end of sdfat_api.c */

397
fs/sdfat/api.h Normal file
View File

@ -0,0 +1,397 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SDFAT_API_H
#define _SDFAT_API_H
#include "config.h"
#include "sdfat_fs.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/*----------------------------------------------------------------------*/
/* Configure Constant & Macro Definitions */
/*----------------------------------------------------------------------*/
/* 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
/* Read-ahead related */
/* First config vars. should be pow of 2 */
#define FCACHE_MAX_RA_SIZE (PAGE_SIZE)
#define DCACHE_MAX_RA_SIZE (128*1024)
/*----------------------------------------------------------------------*/
/* Constant & Macro Definitions */
/*----------------------------------------------------------------------*/
/* type values */
#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
/* eio values */
#define SDFAT_EIO_NONE (0x00000000)
#define SDFAT_EIO_READ (0x00000001)
#define SDFAT_EIO_WRITE (0x00000002)
#define SDFAT_EIO_BDI (0x00000004)
/* modes for volume allocation unit status */
#define VOL_AU_STAT_TOTAL (0)
#define VOL_AU_STAT_CLEAN (1)
#define VOL_AU_STAT_FULL (2)
/*----------------------------------------------------------------------*/
/* NLS Type Definitions */
/*----------------------------------------------------------------------*/
/* DOS name structure */
typedef struct {
u8 name[DOS_NAME_LENGTH];
u8 name_case;
} DOS_NAME_T;
/* unicode name structure */
typedef struct {
u16 name[MAX_NAME_LENGTH+3]; /* +3 for null and for converting */
u16 name_hash;
u8 name_len;
} UNI_NAME_T;
/*----------------------------------------------------------------------*/
/* Type Definitions */
/*----------------------------------------------------------------------*/
/* should be merged it to DATE_TIME_T */
typedef struct {
u16 sec; /* 0 ~ 59 */
u16 min; /* 0 ~ 59 */
u16 hour; /* 0 ~ 23 */
u16 day; /* 1 ~ 31 */
u16 mon; /* 1 ~ 12 */
u16 year; /* 0 ~ 127 (since 1980) */
} TIMESTAMP_T;
typedef struct {
u16 Year;
u16 Month;
u16 Day;
u16 Hour;
u16 Minute;
u16 Second;
u16 MilliSecond;
} DATE_TIME_T;
typedef struct {
u32 Offset; // start sector number of the partition
u32 Size; // in sectors
} PART_INFO_T;
typedef struct {
u32 SecSize; // sector size in bytes
u32 DevSize; // block device size in sectors
} DEV_INFO_T;
typedef struct {
u32 FatType;
u32 ClusterSize;
u32 NumClusters;
u32 FreeClusters;
u32 UsedClusters;
} VOL_INFO_T;
/* directory structure */
typedef struct {
u32 dir;
s32 size;
u8 flags;
} CHAIN_T;
/* hint structure */
typedef struct {
u32 clu;
union {
s32 off; // cluster offset
s32 eidx; // entry index
};
} HINT_T;
typedef struct {
spinlock_t cache_lru_lock;
struct list_head cache_lru;
s32 nr_caches;
u32 cache_valid_id; // for avoiding the race between alloc and free
} EXTENT_T;
/* first empty entry hint information */
typedef struct {
s32 eidx; // entry index of a directory
s32 count; // count of continuous empty entry
CHAIN_T cur; // the cluster that first empty slot exists in
} HINT_FEMP_T;
/* file id structure */
typedef struct {
CHAIN_T dir;
s32 entry;
u32 type;
u32 attr;
u32 start_clu;
u64 size;
u8 flags;
u8 reserved[3]; // padding
u32 version; // the copy of low 32bit of i_version to check the validation of hint_stat
s64 rwoffset; // file offset or dentry index for readdir
EXTENT_T extent; // extent cache for a file
HINT_T hint_bmap; // hint for cluster last accessed
HINT_T hint_stat; // hint for entry index we try to lookup next time
HINT_FEMP_T hint_femp; // hint for first empty entry
} FILE_ID_T;
typedef struct {
s8 *lfn;
s8 *sfn;
s32 lfnbuf_len; //usally MAX_UNINAME_BUF_SIZE
s32 sfnbuf_len; //usally MAX_DOSNAME_BUF_SIZE, used only for vfat, not for exfat
} DENTRY_NAMEBUF_T;
typedef struct {
u32 Attr;
u64 Size;
u32 NumSubdirs;
DATE_TIME_T CreateTimestamp;
DATE_TIME_T ModifyTimestamp;
DATE_TIME_T AccessTimestamp;
DENTRY_NAMEBUF_T NameBuf;
} DIR_ENTRY_T;
/* cache information */
typedef struct __cache_entry {
struct __cache_entry *next;
struct __cache_entry *prev;
struct {
struct __cache_entry *next;
struct __cache_entry *prev;
} hash;
u32 sec;
u32 flag;
struct buffer_head *bh;
} cache_ent_t;
/*----------------------------------------------------------------------*/
/* Type Definitions : Wrapper & In-Core */
/*----------------------------------------------------------------------*/
typedef struct __FATENT_OPS_T {
s32 (*ent_get)(struct super_block *sb, u32 loc, u32 *content);
s32 (*ent_set)(struct super_block *sb, u32 loc, u32 content);
} FATENT_OPS_T;
typedef struct {
s32 (*alloc_cluster)(struct super_block *, s32, CHAIN_T *, int);
s32 (*free_cluster)(struct super_block *, CHAIN_T *, s32);
s32 (*count_used_clusters)(struct super_block *, u32 *);
s32 (*init_dir_entry)(struct super_block *, CHAIN_T *, s32, u32, u32, u64);
s32 (*init_ext_entry)(struct super_block *, CHAIN_T *, s32, s32, UNI_NAME_T *, DOS_NAME_T *);
s32 (*find_dir_entry)(struct super_block *, FILE_ID_T *, CHAIN_T *, UNI_NAME_T *, s32, DOS_NAME_T *, u32);
s32 (*delete_dir_entry)(struct super_block *, CHAIN_T *, s32, s32, s32);
void (*get_uniname_from_ext_entry)(struct super_block *, CHAIN_T *, s32, u16 *);
s32 (*count_ext_entries)(struct super_block *, CHAIN_T *, s32, DENTRY_T *);
s32 (*calc_num_entries)(UNI_NAME_T *);
s32 (*check_max_dentries)(FILE_ID_T *);
u32 (*get_entry_type)(DENTRY_T *);
void (*set_entry_type)(DENTRY_T *, u32);
u32 (*get_entry_attr)(DENTRY_T *);
void (*set_entry_attr)(DENTRY_T *, u32);
u8 (*get_entry_flag)(DENTRY_T *);
void (*set_entry_flag)(DENTRY_T *, u8);
u32 (*get_entry_clu0)(DENTRY_T *);
void (*set_entry_clu0)(DENTRY_T *, u32);
u64 (*get_entry_size)(DENTRY_T *);
void (*set_entry_size)(DENTRY_T *, u64);
void (*get_entry_time)(DENTRY_T *, TIMESTAMP_T *, u8);
void (*set_entry_time)(DENTRY_T *, TIMESTAMP_T *, u8);
u32 (*get_au_stat)(struct super_block *, s32);
} FS_FUNC_T;
typedef struct __FS_INFO_T {
s32 bd_opened; // opened or not
u32 vol_type; // volume FAT type
u32 vol_id; // volume serial number
u32 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 sect_per_clus; // cluster size in sectors
u32 sect_per_clus_bits;
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; // buffer_head of 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
u32 prev_eio; // block device operation error flag
FS_FUNC_T *fs_func;
FATENT_OPS_T *fatent_ops;
s32 reserved_clusters; // # of reserved clusters (DA)
void *amap; // AU Allocation Map
/* fat cache */
struct {
cache_ent_t pool[FAT_CACHE_SIZE];
cache_ent_t lru_list;
cache_ent_t hash_list[FAT_CACHE_HASH_SIZE];
} fcache;
/* meta cache */
struct {
cache_ent_t pool[BUF_CACHE_SIZE];
cache_ent_t lru_list;
cache_ent_t keep_list; // CACHEs in this list will not be kicked by normal lru operations
cache_ent_t hash_list[BUF_CACHE_HASH_SIZE];
} dcache;
} FS_INFO_T;
/*======================================================================*/
/* */
/* API FUNCTION DECLARATIONS */
/* (CHANGE THIS PART IF REQUIRED) */
/* */
/*======================================================================*/
/*----------------------------------------------------------------------*/
/* External Function Declarations */
/*----------------------------------------------------------------------*/
/* file system initialization & shutdown functions */
s32 fsapi_init(void);
s32 fsapi_shutdown(void);
/* volume management functions */
s32 fsapi_mount(struct super_block *sb);
s32 fsapi_umount(struct super_block *sb);
s32 fsapi_statfs(struct super_block *sb, VOL_INFO_T *info);
s32 fsapi_sync_fs(struct super_block *sb, s32 do_sync);
s32 fsapi_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync);
/* file management functions */
s32 fsapi_lookup(struct inode *inode, u8 *path, FILE_ID_T *fid);
s32 fsapi_create(struct inode *inode, u8 *path, u8 mode, FILE_ID_T *fid);
s32 fsapi_read_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount);
s32 fsapi_write_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount);
s32 fsapi_remove(struct inode *inode, FILE_ID_T *fid); /* unlink and truncate */
s32 fsapi_truncate(struct inode *inode, u64 old_size, u64 new_size);
s32 fsapi_rename(struct inode *old_parent_inode, FILE_ID_T *fid,
struct inode *new_parent_inode, struct dentry *new_dentry);
s32 fsapi_unlink(struct inode *inode, FILE_ID_T *fid);
s32 fsapi_read_inode(struct inode *inode, DIR_ENTRY_T *info);
s32 fsapi_write_inode(struct inode *inode, DIR_ENTRY_T *info, int sync);
s32 fsapi_map_clus(struct inode *inode, s32 clu_offset, u32 *clu, int dest);
s32 fsapi_reserve_clus(struct inode *inode);
/* directory management functions */
s32 fsapi_mkdir(struct inode *inode, u8 *path, FILE_ID_T *fid);
s32 fsapi_readdir(struct inode *inode, DIR_ENTRY_T *dir_entry);
s32 fsapi_rmdir(struct inode *inode, FILE_ID_T *fid);
/* FAT & buf cache functions */
s32 fsapi_cache_flush(struct super_block *sb, int do_sync);
s32 fsapi_cache_release(struct super_block *sb);
/* extra info functions */
u32 fsapi_get_au_stat(struct super_block *sb, s32 mode);
/* extent cache functions */
void fsapi_invalidate_extent(struct inode *inode);
#ifdef CONFIG_SDFAT_DFR
/*----------------------------------------------------------------------*/
/* Defragmentation related */
/*----------------------------------------------------------------------*/
s32 fsapi_dfr_get_info(struct super_block *sb, void *arg);
s32 fsapi_dfr_scan_dir(struct super_block *sb, void *args);
s32 fsapi_dfr_validate_clus(struct inode *inode, void *chunk, int skip_prev);
s32 fsapi_dfr_reserve_clus(struct super_block *sb, s32 nr_clus);
s32 fsapi_dfr_mark_ignore(struct super_block *sb, unsigned int clus);
void fsapi_dfr_unmark_ignore_all(struct super_block *sb);
s32 fsapi_dfr_map_clus(struct inode *inode, u32 clu_offset, u32 *clu);
void fsapi_dfr_writepage_endio(struct page *page);
void fsapi_dfr_update_fat_prev(struct super_block *sb, int force);
void fsapi_dfr_update_fat_next(struct super_block *sb);
void fsapi_dfr_check_discard(struct super_block *sb);
void fsapi_dfr_free_clus(struct super_block *sb, u32 clus);
s32 fsapi_dfr_check_dfr_required(struct super_block *sb, int *totalau, int *cleanau, int *fullau);
s32 fsapi_dfr_check_dfr_on(struct inode *inode, loff_t start, loff_t end, s32 cancel, const char *caller);
#ifdef CONFIG_SDFAT_DFR_DEBUG
void fsapi_dfr_spo_test(struct super_block *sb, int flag, const char *caller);
#endif /* CONFIG_SDFAT_DFR_DEBUG */
#endif /* CONFIG_SDFAT_DFR */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _SDFAT_API_H */
/* end of api.h */

416
fs/sdfat/blkdev.c Normal file
View File

@ -0,0 +1,416 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/************************************************************************/
/* */
/* PROJECT : exFAT & FAT12/16/32 File System */
/* FILE : blkdev.c */
/* PURPOSE : sdFAT Block Device Driver Glue Layer */
/* */
/*----------------------------------------------------------------------*/
/* NOTES */
/* */
/************************************************************************/
#include <linux/blkdev.h>
#include <linux/log2.h>
#include <linux/backing-dev.h>
#include "sdfat.h"
/*----------------------------------------------------------------------*/
/* Constant & Macro Definitions */
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* Global Variable Definitions */
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* Local Variable Definitions */
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY */
/************************************************************************/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
/* EMPTY */
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) */
static struct backing_dev_info *inode_to_bdi(struct inode *bd_inode)
{
return bd_inode->i_mapping->backing_dev_info;
}
#endif
/*======================================================================*/
/* Function Definitions */
/*======================================================================*/
s32 bdev_open_dev(struct super_block *sb)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
if (fsi->bd_opened)
return 0;
fsi->bd_opened = true;
return 0;
}
s32 bdev_close_dev(struct super_block *sb)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
fsi->bd_opened = false;
return 0;
}
static inline s32 block_device_ejected(struct super_block *sb)
{
struct inode *bd_inode = sb->s_bdev->bd_inode;
struct backing_dev_info *bdi = inode_to_bdi(bd_inode);
return (bdi->dev == NULL);
}
s32 bdev_check_bdi_valid(struct super_block *sb)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
if (block_device_ejected(sb)) {
if (!(fsi->prev_eio & SDFAT_EIO_BDI)) {
fsi->prev_eio |= SDFAT_EIO_BDI;
sdfat_log_msg(sb, KERN_ERR, "%s: block device is "
"eliminated.(bdi:%p)", __func__, sb->s_bdi);
sdfat_debug_warn_on(1);
}
return -ENXIO;
}
return 0;
}
/* Make a readahead request */
s32 bdev_readahead(struct super_block *sb, u32 secno, u32 num_secs)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
u32 sects_per_page = (PAGE_SIZE >> sb->s_blocksize_bits);
struct blk_plug plug;
u32 i;
if (!fsi->bd_opened)
return -EIO;
blk_start_plug(&plug);
for (i = 0; i < num_secs; i++) {
if (i && !(i & (sects_per_page - 1)))
blk_flush_plug(current);
sb_breadahead(sb, secno + i);
}
blk_finish_plug(&plug);
return 0;
}
s32 bdev_mread(struct super_block *sb, u32 secno, struct buffer_head **bh, u32 num_secs, s32 read)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
u8 blksize_bits = sb->s_blocksize_bits;
#ifdef CONFIG_SDFAT_DBG_IOCTL
struct sdfat_sb_info *sbi = SDFAT_SB(sb);
long flags = sbi->debug_flags;
if (flags & SDFAT_DEBUGFLAGS_ERROR_RW)
return -EIO;
#endif /* CONFIG_SDFAT_DBG_IOCTL */
if (!fsi->bd_opened)
return -EIO;
brelse(*bh);
if (read)
*bh = __bread(sb->s_bdev, secno, num_secs << blksize_bits);
else
*bh = __getblk(sb->s_bdev, secno, num_secs << blksize_bits);
/* read successfully */
if (*bh)
return 0;
/*
* patch 1.2.4 : reset ONCE warning message per volume.
*/
if (!(fsi->prev_eio & SDFAT_EIO_READ)) {
fsi->prev_eio |= SDFAT_EIO_READ;
sdfat_log_msg(sb, KERN_ERR, "%s: No bh. I/O error.", __func__);
sdfat_debug_warn_on(1);
}
return -EIO;
}
s32 bdev_mwrite(struct super_block *sb, u32 secno, struct buffer_head *bh, u32 num_secs, s32 sync)
{
s32 count;
struct buffer_head *bh2;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
#ifdef CONFIG_SDFAT_DBG_IOCTL
struct sdfat_sb_info *sbi = SDFAT_SB(sb);
long flags = sbi->debug_flags;
if (flags & SDFAT_DEBUGFLAGS_ERROR_RW)
return -EIO;
#endif /* CONFIG_SDFAT_DBG_IOCTL */
if (!fsi->bd_opened)
return -EIO;
if (secno == bh->b_blocknr) {
set_buffer_uptodate(bh);
mark_buffer_dirty(bh);
if (sync && (sync_dirty_buffer(bh) != 0))
return -EIO;
} else {
count = num_secs << sb->s_blocksize_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:
/*
* patch 1.2.4 : reset ONCE warning message per volume.
*/
if (!(fsi->prev_eio & SDFAT_EIO_WRITE)) {
fsi->prev_eio |= SDFAT_EIO_WRITE;
sdfat_log_msg(sb, KERN_ERR, "%s: No bh. I/O error.", __func__);
sdfat_debug_warn_on(1);
}
return -EIO;
}
s32 bdev_sync_all(struct super_block *sb)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
#ifdef CONFIG_SDFAT_DBG_IOCTL
struct sdfat_sb_info *sbi = SDFAT_SB(sb);
long flags = sbi->debug_flags;
if (flags & SDFAT_DEBUGFLAGS_ERROR_RW)
return -EIO;
#endif /* CONFIG_SDFAT_DBG_IOCTL */
if (!fsi->bd_opened)
return -EIO;
return sync_blockdev(sb->s_bdev);
}
/*
* Sector Read/Write Functions
*/
s32 read_sect(struct super_block *sb, u32 sec, struct buffer_head **bh, s32 read)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
BUG_ON(!bh);
if ((sec >= fsi->num_sectors) && (fsi->num_sectors > 0)) {
sdfat_fs_error_ratelimit(sb,
"%s: out of range (sect:%u)", __func__, sec);
return -EIO;
}
if (bdev_mread(sb, sec, bh, 1, read)) {
sdfat_fs_error_ratelimit(sb,
"%s: I/O error (sect:%u)", __func__, sec);
return -EIO;
}
return 0;
}
s32 write_sect(struct super_block *sb, u32 sec, struct buffer_head *bh, s32 sync)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
BUG_ON(!bh);
if ((sec >= fsi->num_sectors) && (fsi->num_sectors > 0)) {
sdfat_fs_error_ratelimit(sb,
"%s: out of range (sect:%u)", __func__, sec);
return -EIO;
}
if (bdev_mwrite(sb, sec, bh, 1, sync)) {
sdfat_fs_error_ratelimit(sb, "%s: I/O error (sect:%u)",
__func__, sec);
return -EIO;
}
return 0;
}
s32 read_msect(struct super_block *sb, u32 sec, struct buffer_head **bh, s32 num_secs, s32 read)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
BUG_ON(!bh);
if (((sec+num_secs) > fsi->num_sectors) && (fsi->num_sectors > 0)) {
sdfat_fs_error_ratelimit(sb, "%s: out of range(sect:%u len:%d)",
__func__, sec, num_secs);
return -EIO;
}
if (bdev_mread(sb, sec, bh, num_secs, read)) {
sdfat_fs_error_ratelimit(sb, "%s: I/O error (sect:%u len:%d)",
__func__, sec, num_secs);
return -EIO;
}
return 0;
}
s32 write_msect(struct super_block *sb, u32 sec, struct buffer_head *bh, s32 num_secs, s32 sync)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
BUG_ON(!bh);
if (((sec+num_secs) > fsi->num_sectors) && (fsi->num_sectors > 0)) {
sdfat_fs_error_ratelimit(sb, "%s: out of range(sect:%u len:%d)",
__func__, sec, num_secs);
return -EIO;
}
if (bdev_mwrite(sb, sec, bh, num_secs, sync)) {
sdfat_fs_error_ratelimit(sb, "%s: I/O error (sect:%u len:%d)",
__func__, sec, num_secs);
return -EIO;
}
return 0;
}
static inline void __blkdev_write_bhs(struct buffer_head **bhs, s32 nr_bhs)
{
s32 i;
for (i = 0; i < nr_bhs; i++)
write_dirty_buffer(bhs[i], WRITE);
}
static inline s32 __blkdev_sync_bhs(struct buffer_head **bhs, s32 nr_bhs)
{
s32 i, err = 0;
for (i = 0; i < nr_bhs; i++) {
wait_on_buffer(bhs[i]);
if (!err && !buffer_uptodate(bhs[i]))
err = -EIO;
}
return err;
}
static inline s32 __buffer_zeroed(struct super_block *sb, u32 blknr, s32 num_secs)
{
struct buffer_head *bhs[MAX_BUF_PER_PAGE];
s32 nr_bhs = MAX_BUF_PER_PAGE;
u32 last_blknr = blknr + num_secs;
s32 err, i, n;
struct blk_plug plug;
/* Zeroing the unused blocks on this cluster */
n = 0;
blk_start_plug(&plug);
while (blknr < last_blknr) {
bhs[n] = sb_getblk(sb, blknr);
if (!bhs[n]) {
err = -ENOMEM;
blk_finish_plug(&plug);
goto error;
}
memset(bhs[n]->b_data, 0, sb->s_blocksize);
set_buffer_uptodate(bhs[n]);
mark_buffer_dirty(bhs[n]);
n++;
blknr++;
if (blknr == last_blknr)
break;
if (n == nr_bhs) {
__blkdev_write_bhs(bhs, n);
for (i = 0; i < n; i++)
brelse(bhs[i]);
n = 0;
}
}
__blkdev_write_bhs(bhs, n);
blk_finish_plug(&plug);
err = __blkdev_sync_bhs(bhs, n);
if (err)
goto error;
for (i = 0; i < n; i++)
brelse(bhs[i]);
return 0;
error:
EMSG("%s: failed zeroed sect %u\n", __func__, blknr);
for (i = 0; i < n; i++)
bforget(bhs[i]);
return err;
}
s32 write_msect_zero(struct super_block *sb, u32 sec, s32 num_secs)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
if (((sec+num_secs) > fsi->num_sectors) && (fsi->num_sectors > 0)) {
sdfat_fs_error_ratelimit(sb, "%s: out of range(sect:%u len:%d)",
__func__, sec, num_secs);
return -EIO;
}
/* Just return -EAGAIN if it is failed */
if (__buffer_zeroed(sb, sec, num_secs))
return -EAGAIN;
return 0;
} /* end of write_msect_zero */
/* end of blkdev.c */

842
fs/sdfat/cache.c Normal file
View File

@ -0,0 +1,842 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/************************************************************************/
/* */
/* PROJECT : exFAT & FAT12/16/32 File System */
/* FILE : cache.c */
/* PURPOSE : sdFAT Cache Manager */
/* (FAT Cache & Buffer Cache) */
/* */
/*----------------------------------------------------------------------*/
/* NOTES */
/* */
/* */
/************************************************************************/
#include <linux/swap.h> /* for mark_page_accessed() */
#include <asm/unaligned.h>
#include "sdfat.h"
#include "core.h"
#define DEBUG_HASH_LIST
#define DEBUG_HASH_PREV (0xAAAA5555)
#define DEBUG_HASH_NEXT (0x5555AAAA)
/*----------------------------------------------------------------------*/
/* Global Variable Definitions */
/*----------------------------------------------------------------------*/
/* All buffer structures are protected w/ fsi->v_sem */
/*----------------------------------------------------------------------*/
/* Local Variable Definitions */
/*----------------------------------------------------------------------*/
#define LOCKBIT (0x01)
#define DIRTYBIT (0x02)
#define KEEPBIT (0x04)
/*----------------------------------------------------------------------*/
/* Cache handling function declarations */
/*----------------------------------------------------------------------*/
static cache_ent_t *__fcache_find(struct super_block *sb, u32 sec);
static cache_ent_t *__fcache_get(struct super_block *sb, u32 sec);
static void __fcache_insert_hash(struct super_block *sb, cache_ent_t *bp);
static void __fcache_remove_hash(cache_ent_t *bp);
static cache_ent_t *__dcache_find(struct super_block *sb, u32 sec);
static cache_ent_t *__dcache_get(struct super_block *sb, u32 sec);
static void __dcache_insert_hash(struct super_block *sb, cache_ent_t *bp);
static void __dcache_remove_hash(cache_ent_t *bp);
/*----------------------------------------------------------------------*/
/* Static functions */
/*----------------------------------------------------------------------*/
static void push_to_mru(cache_ent_t *bp, cache_ent_t *list)
{
bp->next = list->next;
bp->prev = list;
list->next->prev = bp;
list->next = bp;
}
static void push_to_lru(cache_ent_t *bp, cache_ent_t *list)
{
bp->prev = list->prev;
bp->next = list;
list->prev->next = bp;
list->prev = bp;
}
static void move_to_mru(cache_ent_t *bp, cache_ent_t *list)
{
bp->prev->next = bp->next;
bp->next->prev = bp->prev;
push_to_mru(bp, list);
}
static void move_to_lru(cache_ent_t *bp, cache_ent_t *list)
{
bp->prev->next = bp->next;
bp->next->prev = bp->prev;
push_to_lru(bp, list);
}
static inline s32 __check_hash_valid(cache_ent_t *bp)
{
#ifdef DEBUG_HASH_LIST
if ((bp->hash.next == (cache_ent_t *)DEBUG_HASH_NEXT) ||
(bp->hash.prev == (cache_ent_t *)DEBUG_HASH_PREV)) {
return -EINVAL;
}
#endif
if ((bp->hash.next == bp) || (bp->hash.prev == bp))
return -EINVAL;
return 0;
}
static inline void __remove_from_hash(cache_ent_t *bp)
{
(bp->hash.prev)->hash.next = bp->hash.next;
(bp->hash.next)->hash.prev = bp->hash.prev;
bp->hash.next = bp;
bp->hash.prev = bp;
#ifdef DEBUG_HASH_LIST
bp->hash.next = (cache_ent_t *)DEBUG_HASH_NEXT;
bp->hash.prev = (cache_ent_t *)DEBUG_HASH_PREV;
#endif
}
/* Do FAT mirroring (don't sync)
* sec: sector No. in FAT1
* bh: bh of sec.
*/
static inline s32 __fat_copy(struct super_block *sb, u32 sec, struct buffer_head *bh, int sync)
{
#ifdef CONFIG_SDFAT_FAT_MIRRORING
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
int sec2;
if (fsi->FAT2_start_sector != fsi->FAT1_start_sector) {
sec2 = sec - fsi->FAT1_start_sector + fsi->FAT2_start_sector;
BUG_ON(sec2 != (sec + fsi->num_FAT_sectors));
MMSG("BD: fat mirroring (%d in FAT1, %d in FAT2)\n", sec, sec2);
if (write_sect(sb, sec2, bh, sync))
return -EIO;
}
#else
/* DO NOTHING */
#endif
return 0;
} /* end of __fat_copy */
/*
* returns 1, if bp is flushed
* returns 0, if bp is not dirty
* returns -1, if error occurs
*/
static s32 __fcache_ent_flush(struct super_block *sb, cache_ent_t *bp, u32 sync)
{
if (!(bp->flag & DIRTYBIT))
return 0;
#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
// Make buffer dirty (XXX: Naive impl.)
if (write_sect(sb, bp->sec, bp->bh, 0))
return -EIO;
if (__fat_copy(sb, bp->sec, bp->bh, 0))
return -EIO;
#endif
bp->flag &= ~(DIRTYBIT);
if (sync)
sync_dirty_buffer(bp->bh);
return 1;
}
static s32 __fcache_ent_discard(struct super_block *sb, cache_ent_t *bp)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
__fcache_remove_hash(bp);
bp->sec = ~0;
bp->flag = 0;
if (bp->bh) {
__brelse(bp->bh);
bp->bh = NULL;
}
move_to_lru(bp, &fsi->fcache.lru_list);
return 0;
}
u8 *fcache_getblk(struct super_block *sb, u32 sec)
{
cache_ent_t *bp;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
u32 page_ra_count = FCACHE_MAX_RA_SIZE >> sb->s_blocksize_bits;
bp = __fcache_find(sb, sec);
if (bp) {
if (bdev_check_bdi_valid(sb)) {
__fcache_ent_flush(sb, bp, 0);
__fcache_ent_discard(sb, bp);
return NULL;
}
move_to_mru(bp, &fsi->fcache.lru_list);
return bp->bh->b_data;
}
bp = __fcache_get(sb, sec);
if (!__check_hash_valid(bp))
__fcache_remove_hash(bp);
bp->sec = sec;
bp->flag = 0;
__fcache_insert_hash(sb, bp);
/* Naive FAT read-ahead (increase I/O unit to page_ra_count) */
if ((sec & (page_ra_count - 1)) == 0)
bdev_readahead(sb, sec, page_ra_count);
/*
* patch 1.2.4 : buffer_head null pointer exception problem.
*
* When read_sect is failed, fcache should be moved to
* EMPTY hash_list and the first of lru_list.
*/
if (read_sect(sb, sec, &(bp->bh), 1)) {
__fcache_ent_discard(sb, bp);
return NULL;
}
return bp->bh->b_data;
}
static inline int __mark_delayed_dirty(struct super_block *sb, cache_ent_t *bp)
{
#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
if (fsi->vol_type == EXFAT)
return -ENOTSUPP;
bp->flag |= DIRTYBIT;
return 0;
#else
return -ENOTSUPP;
#endif
}
s32 fcache_modify(struct super_block *sb, u32 sec)
{
cache_ent_t *bp;
bp = __fcache_find(sb, sec);
if (!bp) {
sdfat_fs_error(sb, "Can`t find fcache (sec 0x%08x)", sec);
return -EIO;
}
if (!__mark_delayed_dirty(sb, bp))
return 0;
if (write_sect(sb, sec, bp->bh, 0))
return -EIO;
if (__fat_copy(sb, sec, bp->bh, 0))
return -EIO;
return 0;
}
/*======================================================================*/
/* Cache Initialization Functions */
/*======================================================================*/
s32 meta_cache_init(struct super_block *sb)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
s32 i;
/* LRU list */
fsi->fcache.lru_list.next = &fsi->fcache.lru_list;
fsi->fcache.lru_list.prev = fsi->fcache.lru_list.next;
for (i = 0; i < FAT_CACHE_SIZE; i++) {
fsi->fcache.pool[i].sec = ~0;
fsi->fcache.pool[i].flag = 0;
fsi->fcache.pool[i].bh = NULL;
fsi->fcache.pool[i].prev = NULL;
fsi->fcache.pool[i].next = NULL;
push_to_mru(&(fsi->fcache.pool[i]), &fsi->fcache.lru_list);
}
fsi->dcache.lru_list.next = &fsi->dcache.lru_list;
fsi->dcache.lru_list.prev = fsi->dcache.lru_list.next;
fsi->dcache.keep_list.next = &fsi->dcache.keep_list;
fsi->dcache.keep_list.prev = fsi->dcache.keep_list.next;
// Initially, all the BUF_CACHEs are in the LRU list
for (i = 0; i < BUF_CACHE_SIZE; i++) {
fsi->dcache.pool[i].sec = ~0;
fsi->dcache.pool[i].flag = 0;
fsi->dcache.pool[i].bh = NULL;
fsi->dcache.pool[i].prev = NULL;
fsi->dcache.pool[i].next = NULL;
push_to_mru(&(fsi->dcache.pool[i]), &fsi->dcache.lru_list);
}
/* HASH list */
for (i = 0; i < FAT_CACHE_HASH_SIZE; i++) {
fsi->fcache.hash_list[i].sec = ~0;
fsi->fcache.hash_list[i].hash.next = &(fsi->fcache.hash_list[i]);
;
fsi->fcache.hash_list[i].hash.prev = fsi->fcache.hash_list[i].hash.next;
}
for (i = 0; i < FAT_CACHE_SIZE; i++)
__fcache_insert_hash(sb, &(fsi->fcache.pool[i]));
for (i = 0; i < BUF_CACHE_HASH_SIZE; i++) {
fsi->dcache.hash_list[i].sec = ~0;
fsi->dcache.hash_list[i].hash.next = &(fsi->dcache.hash_list[i]);
fsi->dcache.hash_list[i].hash.prev = fsi->dcache.hash_list[i].hash.next;
}
for (i = 0; i < BUF_CACHE_SIZE; i++)
__dcache_insert_hash(sb, &(fsi->dcache.pool[i]));
return 0;
}
s32 meta_cache_shutdown(struct super_block *sb)
{
return 0;
}
/*======================================================================*/
/* FAT Read/Write Functions */
/*======================================================================*/
s32 fcache_release_all(struct super_block *sb)
{
s32 ret = 0;
cache_ent_t *bp;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
s32 dirtycnt = 0;
bp = fsi->fcache.lru_list.next;
while (bp != &fsi->fcache.lru_list) {
s32 ret_tmp = __fcache_ent_flush(sb, bp, 0);
if (ret_tmp < 0)
ret = ret_tmp;
else
dirtycnt += ret_tmp;
bp->sec = ~0;
bp->flag = 0;
if (bp->bh) {
__brelse(bp->bh);
bp->bh = NULL;
}
bp = bp->next;
}
DMSG("BD:Release / dirty fat cache: %d (err:%d)\n", dirtycnt, ret);
return ret;
}
/* internal DIRTYBIT marked => bh dirty */
s32 fcache_flush(struct super_block *sb, u32 sync)
{
s32 ret = 0;
cache_ent_t *bp;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
s32 dirtycnt = 0;
bp = fsi->fcache.lru_list.next;
while (bp != &fsi->fcache.lru_list) {
ret = __fcache_ent_flush(sb, bp, sync);
if (ret < 0)
break;
dirtycnt += ret;
bp = bp->next;
}
MMSG("BD: flush / dirty fat cache: %d (err:%d)\n", dirtycnt, ret);
return ret;
}
static cache_ent_t *__fcache_find(struct super_block *sb, u32 sec)
{
s32 off;
cache_ent_t *bp, *hp;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
off = (sec + (sec >> fsi->sect_per_clus_bits)) & (FAT_CACHE_HASH_SIZE - 1);
hp = &(fsi->fcache.hash_list[off]);
for (bp = hp->hash.next; bp != hp; bp = bp->hash.next) {
if (bp->sec == sec) {
/*
* patch 1.2.4 : for debugging
*/
WARN(!bp->bh, "[SDFAT] fcache has no bh. "
"It will make system panic.\n");
touch_buffer(bp->bh);
return bp;
}
}
return NULL;
}
static cache_ent_t *__fcache_get(struct super_block *sb, u32 sec)
{
cache_ent_t *bp;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
bp = fsi->fcache.lru_list.prev;
#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
while (bp->flag & DIRTYBIT) {
cache_ent_t *bp_prev = bp->prev;
bp = bp_prev;
if (bp == &fsi->fcache.lru_list) {
DMSG("BD: fat cache flooding\n");
fcache_flush(sb, 0); // flush all dirty FAT caches
bp = fsi->fcache.lru_list.prev;
}
}
#endif
// if (bp->flag & DIRTYBIT)
// sync_dirty_buffer(bp->bh);
move_to_mru(bp, &fsi->fcache.lru_list);
return bp;
}
static void __fcache_insert_hash(struct super_block *sb, cache_ent_t *bp)
{
s32 off;
cache_ent_t *hp;
FS_INFO_T *fsi;
fsi = &(SDFAT_SB(sb)->fsi);
off = (bp->sec + (bp->sec >> fsi->sect_per_clus_bits)) & (FAT_CACHE_HASH_SIZE-1);
hp = &(fsi->fcache.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 __fcache_remove_hash(cache_ent_t *bp)
{
#ifdef DEBUG_HASH_LIST
if ((bp->hash.next == (cache_ent_t *)DEBUG_HASH_NEXT) ||
(bp->hash.prev == (cache_ent_t *)DEBUG_HASH_PREV)) {
EMSG("%s: FATAL: tried to remove already-removed-cache-entry"
"(bp:%p)\n", __func__, bp);
return;
}
#endif
WARN_ON(bp->flag & DIRTYBIT);
__remove_from_hash(bp);
}
/*======================================================================*/
/* Buffer Read/Write Functions */
/*======================================================================*/
/* Read-ahead a cluster */
s32 dcache_readahead(struct super_block *sb, u32 sec)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
struct buffer_head *bh;
u32 max_ra_count = DCACHE_MAX_RA_SIZE >> sb->s_blocksize_bits;
u32 page_ra_count = PAGE_SIZE >> sb->s_blocksize_bits;
u32 adj_ra_count = max(fsi->sect_per_clus, page_ra_count);
u32 ra_count = min(adj_ra_count, max_ra_count);
/* Read-ahead is not required */
if (fsi->sect_per_clus == 1)
return 0;
if (sec < fsi->data_start_sector) {
EMSG("BD: %s: requested sector is invalid(sect:%u, root:%u)\n",
__func__, sec, fsi->data_start_sector);
return -EIO;
}
/* Not sector aligned with ra_count, resize ra_count to page size */
if ((sec - fsi->data_start_sector) & (ra_count - 1))
ra_count = page_ra_count;
bh = sb_find_get_block(sb, sec);
if (!bh || !buffer_uptodate(bh))
bdev_readahead(sb, sec, ra_count);
brelse(bh);
return 0;
}
/*
* returns 1, if bp is flushed
* returns 0, if bp is not dirty
* returns -1, if error occurs
*/
static s32 __dcache_ent_flush(struct super_block *sb, cache_ent_t *bp, u32 sync)
{
if (!(bp->flag & DIRTYBIT))
return 0;
#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
// Make buffer dirty (XXX: Naive impl.)
if (write_sect(sb, bp->sec, bp->bh, 0))
return -EIO;
#endif
bp->flag &= ~(DIRTYBIT);
if (sync)
sync_dirty_buffer(bp->bh);
return 1;
}
static s32 __dcache_ent_discard(struct super_block *sb, cache_ent_t *bp)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
MMSG("%s : bp[%p] (sec:%08x flag:%08x bh:%p) list(prev:%p next:%p) "
"hash(prev:%p next:%p)\n", __func__,
bp, bp->sec, bp->flag, bp->bh, bp->prev, bp->next,
bp->hash.prev, bp->hash.next);
__dcache_remove_hash(bp);
bp->sec = ~0;
bp->flag = 0;
if (bp->bh) {
__brelse(bp->bh);
bp->bh = NULL;
}
move_to_lru(bp, &fsi->dcache.lru_list);
return 0;
}
u8 *dcache_getblk(struct super_block *sb, u32 sec)
{
cache_ent_t *bp;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
bp = __dcache_find(sb, sec);
if (bp) {
if (bdev_check_bdi_valid(sb)) {
MMSG("%s: found cache(%p, sect:%u). But invalid BDI\n"
, __func__, bp, sec);
__dcache_ent_flush(sb, bp, 0);
__dcache_ent_discard(sb, bp);
return NULL;
}
if (!(bp->flag & KEEPBIT)) // already in keep list
move_to_mru(bp, &fsi->dcache.lru_list);
return bp->bh->b_data;
}
bp = __dcache_get(sb, sec);
if (!__check_hash_valid(bp))
__dcache_remove_hash(bp);
bp->sec = sec;
bp->flag = 0;
__dcache_insert_hash(sb, bp);
if (read_sect(sb, sec, &(bp->bh), 1)) {
__dcache_ent_discard(sb, bp);
return NULL;
}
return bp->bh->b_data;
}
s32 dcache_modify(struct super_block *sb, u32 sec)
{
s32 ret = -EIO;
cache_ent_t *bp;
set_sb_dirty(sb);
bp = __dcache_find(sb, sec);
if (unlikely(!bp)) {
sdfat_fs_error(sb, "Can`t find dcache (sec 0x%08x)", sec);
return -EIO;
}
#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
if (SDFAT_SB(sb)->fsi.vol_type != EXFAT) {
bp->flag |= DIRTYBIT;
return 0;
}
#endif
ret = write_sect(sb, sec, bp->bh, 0);
if (ret) {
DMSG("%s : failed to modify buffer(err:%d, sec:%u, bp:0x%p)\n",
__func__, ret, sec, bp);
}
return ret;
}
s32 dcache_lock(struct super_block *sb, u32 sec)
{
cache_ent_t *bp;
bp = __dcache_find(sb, sec);
if (likely(bp)) {
bp->flag |= LOCKBIT;
return 0;
}
EMSG("%s : failed to lock buffer(sec:%u, bp:0x%p)\n", __func__, sec, bp);
return -EIO;
}
s32 dcache_unlock(struct super_block *sb, u32 sec)
{
cache_ent_t *bp;
bp = __dcache_find(sb, sec);
if (likely(bp)) {
bp->flag &= ~(LOCKBIT);
return 0;
}
EMSG("%s : failed to unlock buffer (sec:%u, bp:0x%p)\n", __func__, sec, bp);
return -EIO;
}
s32 dcache_release(struct super_block *sb, u32 sec)
{
cache_ent_t *bp;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
bp = __dcache_find(sb, sec);
if (unlikely(!bp))
return -ENOENT;
#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
if (bp->flag & DIRTYBIT) {
if (write_sect(sb, bp->sec, bp->bh, 0))
return -EIO;
}
#endif
bp->sec = ~0;
bp->flag = 0;
if (bp->bh) {
__brelse(bp->bh);
bp->bh = NULL;
}
move_to_lru(bp, &fsi->dcache.lru_list);
return 0;
}
s32 dcache_release_all(struct super_block *sb)
{
s32 ret = 0;
cache_ent_t *bp;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
s32 dirtycnt = 0;
/* Connect list elements:
* LRU list : (A - B - ... - bp_front) + (bp_first + ... + bp_last)
*/
while (fsi->dcache.keep_list.prev != &fsi->dcache.keep_list) {
cache_ent_t *bp_keep = fsi->dcache.keep_list.prev;
// bp_keep->flag &= ~(KEEPBIT); // Will be 0-ed later
move_to_mru(bp_keep, &fsi->dcache.lru_list);
}
bp = fsi->dcache.lru_list.next;
while (bp != &fsi->dcache.lru_list) {
#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
if (bp->flag & DIRTYBIT) {
dirtycnt++;
if (write_sect(sb, bp->sec, bp->bh, 0))
ret = -EIO;
}
#endif
bp->sec = ~0;
bp->flag = 0;
if (bp->bh) {
__brelse(bp->bh);
bp->bh = NULL;
}
bp = bp->next;
}
DMSG("BD:Release / dirty buf cache: %d (err:%d)", dirtycnt, ret);
return ret;
}
s32 dcache_flush(struct super_block *sb, u32 sync)
{
s32 ret = 0;
cache_ent_t *bp;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
s32 dirtycnt = 0;
s32 keepcnt = 0;
/* Connect list elements:
* LRU list : (A - B - ... - bp_front) + (bp_first + ... + bp_last)
*/
while (fsi->dcache.keep_list.prev != &fsi->dcache.keep_list) {
cache_ent_t *bp_keep = fsi->dcache.keep_list.prev;
bp_keep->flag &= ~(KEEPBIT); // Will be 0-ed later
move_to_mru(bp_keep, &fsi->dcache.lru_list);
keepcnt++;
}
bp = fsi->dcache.lru_list.next;
while (bp != &fsi->dcache.lru_list) {
if (bp->flag & DIRTYBIT) {
#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
// Make buffer dirty (XXX: Naive impl.)
if (write_sect(sb, bp->sec, bp->bh, 0)) {
ret = -EIO;
break;
}
#endif
bp->flag &= ~(DIRTYBIT);
dirtycnt++;
if (sync != 0)
sync_dirty_buffer(bp->bh);
}
bp = bp->next;
}
MMSG("BD: flush / dirty dentry cache: %d (%d from keeplist, err:%d)\n",
dirtycnt, keepcnt, ret);
return ret;
}
static cache_ent_t *__dcache_find(struct super_block *sb, u32 sec)
{
s32 off;
cache_ent_t *bp, *hp;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
off = (sec + (sec >> fsi->sect_per_clus_bits)) & (BUF_CACHE_HASH_SIZE - 1);
hp = &(fsi->dcache.hash_list[off]);
for (bp = hp->hash.next; bp != hp; bp = bp->hash.next) {
if (bp->sec == sec) {
touch_buffer(bp->bh);
return bp;
}
}
return NULL;
}
static cache_ent_t *__dcache_get(struct super_block *sb, u32 sec)
{
cache_ent_t *bp;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
bp = fsi->dcache.lru_list.prev;
#ifdef CONFIG_SDFAT_DELAYED_META_DIRTY
while (bp->flag & (DIRTYBIT | LOCKBIT)) {
cache_ent_t *bp_prev = bp->prev; // hold prev
if (bp->flag & DIRTYBIT) {
MMSG("BD: Buf cache => Keep list\n");
bp->flag |= KEEPBIT;
move_to_mru(bp, &fsi->dcache.keep_list);
}
bp = bp_prev;
/* If all dcaches are dirty */
if (bp == &fsi->dcache.lru_list) {
DMSG("BD: buf cache flooding\n");
dcache_flush(sb, 0);
bp = fsi->dcache.lru_list.prev;
}
}
#else
while (bp->flag & LOCKBIT)
bp = bp->prev;
#endif
// if (bp->flag & DIRTYBIT)
// sync_dirty_buffer(bp->bh);
move_to_mru(bp, &fsi->dcache.lru_list);
return bp;
}
static void __dcache_insert_hash(struct super_block *sb, cache_ent_t *bp)
{
s32 off;
cache_ent_t *hp;
FS_INFO_T *fsi;
fsi = &(SDFAT_SB(sb)->fsi);
off = (bp->sec + (bp->sec >> fsi->sect_per_clus_bits)) & (BUF_CACHE_HASH_SIZE-1);
hp = &(fsi->dcache.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 __dcache_remove_hash(cache_ent_t *bp)
{
#ifdef DEBUG_HASH_LIST
if ((bp->hash.next == (cache_ent_t *)DEBUG_HASH_NEXT) ||
(bp->hash.prev == (cache_ent_t *)DEBUG_HASH_PREV)) {
EMSG("%s: FATAL: tried to remove already-removed-cache-entry"
"(bp:%p)\n", __func__, bp);
return;
}
#endif
WARN_ON(bp->flag & DIRTYBIT);
__remove_from_hash(bp);
}
/* end of cache.c */

146
fs/sdfat/config.h Normal file
View File

@ -0,0 +1,146 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SDFAT_CONFIG_H
#define _SDFAT_CONFIG_H
/*======================================================================*/
/* */
/* FFS CONFIGURATIONS */
/* (CHANGE THIS PART IF REQUIRED) */
/* */
/*======================================================================*/
/*----------------------------------------------------------------------*/
/* Feature Config */
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* Debug/Experimental Config */
/*----------------------------------------------------------------------*/
//#define CONFIG_SDFAT_TRACE_IO
//#define CONFIG_SDFAT_TRACE_LOCK /* Trace elapsed time in lock_super(sb) */
/*----------------------------------------------------------------------*/
/* Defragmentation Config */
/*----------------------------------------------------------------------*/
//#define CONFIG_SDFAT_DFR
//#define CONFIG_SDFAT_DFR_PACKING
//#define CONFIG_SDFAT_DFR_DEBUG
/*----------------------------------------------------------------------*/
/* Config for Kernel equal or newer than 3.7 */
/*----------------------------------------------------------------------*/
#ifndef CONFIG_SDFAT_WRITE_SB_INTERVAL_CSECS
#define CONFIG_SDFAT_WRITE_SB_INTERVAL_CSECS (dirty_writeback_interval)
#endif
/*----------------------------------------------------------------------*/
/* Default Kconfig */
/*----------------------------------------------------------------------*/
/* default mount options */
#ifndef CONFIG_SDFAT_DEFAULT_CODEPAGE /* if Kconfig lacked codepage */
#define CONFIG_SDFAT_DEFAULT_CODEPAGE 437
#endif
#ifndef CONFIG_SDFAT_DEFAULT_IOCHARSET /* if Kconfig lacked iocharset */
#define CONFIG_SDFAT_DEFAULT_IOCHARSET "utf8"
#endif
#ifndef CONFIG_SDFAT_FAT32_SHORTNAME_SEQ /* Shortname ~1, ... ~9 have higher
* priority (WIN32/VFAT-like)
*/
//#define CONFIG_SDFAT_FAT32_SHORTNAME_SEQ
#endif
#ifndef CONFIG_SDFAT_ALIGNED_MPAGE_WRITE
//#define CONFIG_SDFAT_ALIGNED_MPAGE_WRITE
#endif
#ifndef CONFIG_SDFAT_FAT_MIRRORING /* if Kconfig lacked fat-mirroring option */
#define CONFIG_SDFAT_FAT_MIRRORING /* Write FAT 1, FAT 2 simultaneously */
#endif
#ifndef CONFIG_SDFAT_DELAYED_META_DIRTY
//#define CONFIG_SDFAT_DELAYED_META_DIRTY /* delayed DIR/FAT dirty support */
#endif
#ifndef CONFIG_SDFAT_SUPPORT_DIR_SYNC
//#define CONFIG_SDFAT_SUPPORT_DIR_SYNC /* support DIR_SYNC */
#endif
#ifndef CONFIG_SDFAT_CHECK_RO_ATTR
//#define CONFIG_SDFAT_CHECK_RO_ATTR
#endif
#ifndef CONFIG_SDFAT_RESTRICT_EXT_ONLY_SFN
#define CONFIG_SDFAT_RESTRICT_EXT_ONLY_SFN
#endif
#ifndef CONFIG_SDFAT_ALLOW_LOOKUP_LOSSY_SFN
//#define CONFIG_SDFAT_ALLOW_LOOKUP_LOSSY_SFN
#endif
#ifndef CONFIG_SDFAT_DBG_SHOW_PID
//#define CONFIG_SDFAT_DBG_SHOW_PID
#endif
#ifndef CONFIG_SDFAT_VIRTUAL_XATTR
//#define CONFIG_SDFAT_VIRTUAL_XATTR
#endif
#ifndef CONFIG_SDFAT_SUPPORT_STLOG
//#define CONFIG_SDFAT_SUPPORT_STLOG
#endif
#ifndef CONFIG_SDFAT_DEBUG
//{
//#define CONFIG_SDFAT_DEBUG
#ifndef CONFIG_SDFAT_DBG_IOCTL
//#define CONFIG_SDFAT_DBG_IOCTL
#endif
#ifndef CONFIG_SDFAT_DBG_MSG
//#define CONFIG_SDFAT_DBG_MSG
#endif
#ifndef CONFIG_SDFAT_DBG_CAREFUL
//#define CONFIG_SDFAT_DBG_CAREFUL
#endif
#ifndef CONFIG_SDFAT_DBG_BUGON
//#define CONFIG_SDFAT_DBG_BUGON
#endif
#ifndef CONFIG_SDFAT_DBG_WARNON
//#define CONFIG_SDFAT_DBG_WARNON
#endif
//}
#endif /* CONFIG_SDFAT_DEBUG */
#ifndef CONFIG_SDFAT_TRACE_SB_LOCK
//#define CONFIG_SDFAT_TRACE_SB_LOCK
#endif
#ifndef CONFIG_SDFAT_TRACE_ELAPSED_TIME
//#define CONFIG_SDFAT_TRACE_ELAPSED_TIME
#endif
#endif /* _SDFAT_CONFIG_H */
/* end of config.h */

3672
fs/sdfat/core.c Normal file

File diff suppressed because it is too large Load Diff

219
fs/sdfat/core.h Normal file
View File

@ -0,0 +1,219 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SDFAT_CORE_H
#define _SDFAT_CORE_H
#include <asm/byteorder.h>
#include "config.h"
#include "api.h"
#include "upcase.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/*----------------------------------------------------------------------*/
/* Constant & Macro Definitions */
/*----------------------------------------------------------------------*/
#define get_next_clus(sb, pclu) fat_ent_get(sb, *(pclu), pclu)
#define get_next_clus_safe(sb, pclu) fat_ent_get_safe(sb, *(pclu), pclu)
/* file status */
/* this prevents
* fscore_write_inode, fscore_map_clus, ... with the unlinked inodes
* from corrupting on-disk dentry data.
*
* The fid->dir value of unlinked inode will be DIR_DELETED
* and those functions must check if fid->dir is valid prior to
* the calling of get_dentry_in_dir()
*/
#define DIR_DELETED 0xFFFF0321
/*----------------------------------------------------------------------*/
/* Type Definitions */
/*----------------------------------------------------------------------*/
#define ES_2_ENTRIES 2
#define ES_3_ENTRIES 3
#define ES_ALL_ENTRIES 0
typedef struct {
u32 sector; // sector number that contains file_entry
s32 offset; // byte offset in the sector
s32 alloc_flag; // flag in stream entry. 01 for cluster chain, 03 for contig. clusteres.
u32 num_entries;
// __buf should be the last member
void *__buf;
} ENTRY_SET_CACHE_T;
/*----------------------------------------------------------------------*/
/* External Function Declarations */
/*----------------------------------------------------------------------*/
/* file system initialization & shutdown functions */
s32 fscore_init(void);
s32 fscore_shutdown(void);
/* chain management */
s32 chain_cont_cluster(struct super_block *sb, u32 chain, s32 len);
/* volume management functions */
s32 fscore_mount(struct super_block *sb);
s32 fscore_umount(struct super_block *sb);
s32 fscore_statfs(struct super_block *sb, VOL_INFO_T *info);
s32 fscore_sync_fs(struct super_block *sb, s32 do_sync);
s32 fscore_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync);
u32 fscore_get_au_stat(struct super_block *sb, s32 mode);
/* file management functions */
s32 fscore_lookup(struct inode *inode, u8 *path, FILE_ID_T *fid);
s32 fscore_create(struct inode *inode, u8 *path, u8 mode, FILE_ID_T *fid);
s32 fscore_read_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount);
s32 fscore_write_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount);
s32 fscore_truncate(struct inode *inode, u64 old_size, u64 new_size);
s32 fscore_rename(struct inode *old_parent_inode, FILE_ID_T *fid,
struct inode *new_parent_inode, struct dentry *new_dentry);
s32 fscore_remove(struct inode *inode, FILE_ID_T *fid);
s32 fscore_read_inode(struct inode *inode, DIR_ENTRY_T *info);
s32 fscore_write_inode(struct inode *inode, DIR_ENTRY_T *info, int sync);
s32 fscore_map_clus(struct inode *inode, s32 clu_offset, u32 *clu, int dest);
s32 fscore_reserve_clus(struct inode *inode);
s32 fscore_unlink(struct inode *inode, FILE_ID_T *fid);
/* directory management functions */
s32 fscore_mkdir(struct inode *inode, u8 *path, FILE_ID_T *fid);
s32 fscore_readdir(struct inode *inode, DIR_ENTRY_T *dir_ent);
s32 fscore_rmdir(struct inode *inode, FILE_ID_T *fid);
/*----------------------------------------------------------------------*/
/* External Function Declarations (NOT TO UPPER LAYER) */
/*----------------------------------------------------------------------*/
/* core.c : core code for common */
/* dir entry management functions */
DENTRY_T *get_dentry_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 *sector);
/* name conversion functions */
void get_uniname_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, u8 mode);
/* file operation functions */
s32 walk_fat_chain(struct super_block *sb, CHAIN_T *p_dir, s32 byte_offset, u32 *clu);
/* sdfat/cache.c */
s32 meta_cache_init(struct super_block *sb);
s32 meta_cache_shutdown(struct super_block *sb);
u8 *fcache_getblk(struct super_block *sb, u32 sec);
s32 fcache_modify(struct super_block *sb, u32 sec);
s32 fcache_release_all(struct super_block *sb);
s32 fcache_flush(struct super_block *sb, u32 sync);
u8 *dcache_getblk(struct super_block *sb, u32 sec);
s32 dcache_modify(struct super_block *sb, u32 sec);
s32 dcache_lock(struct super_block *sb, u32 sec);
s32 dcache_unlock(struct super_block *sb, u32 sec);
s32 dcache_release(struct super_block *sb, u32 sec);
s32 dcache_release_all(struct super_block *sb);
s32 dcache_flush(struct super_block *sb, u32 sync);
s32 dcache_readahead(struct super_block *sb, u32 sec);
/* fatent.c */
s32 fat_ent_ops_init(struct super_block *sb);
s32 fat_ent_get(struct super_block *sb, u32 loc, u32 *content);
s32 fat_ent_set(struct super_block *sb, u32 loc, u32 content);
s32 fat_ent_get_safe(struct super_block *sb, u32 loc, u32 *content);
/* core_fat.c : core code for fat */
s32 fat_generate_dos_name_new(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname, s32 n_entries);
s32 mount_fat16(struct super_block *sb, pbr_t *p_pbr);
s32 mount_fat32(struct super_block *sb, pbr_t *p_pbr);
/* core_exfat.c : core code for exfat */
s32 load_alloc_bmp(struct super_block *sb);
void free_alloc_bmp(struct super_block *sb);
ENTRY_SET_CACHE_T *get_dentry_set_in_dir(struct super_block *sb,
CHAIN_T *p_dir, s32 entry, u32 type, DENTRY_T **file_ep);
void release_dentry_set(ENTRY_SET_CACHE_T *es);
s32 update_dir_chksum(struct super_block *sb, CHAIN_T *p_dir, s32 entry);
s32 update_dir_chksum_with_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es);
bool is_dir_empty(struct super_block *sb, CHAIN_T *p_dir);
s32 mount_exfat(struct super_block *sb, pbr_t *p_pbr);
/* amap_smart.c : creation on mount / destroy on umount */
int amap_create(struct super_block *sb, u32 pack_ratio, u32 sect_per_au, u32 hidden_sect);
void amap_destroy(struct super_block *sb);
/* amap_smart.c : (de)allocation functions */
s32 amap_fat_alloc_cluster(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain, int dest);
s32 amap_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse);/* Not impelmented */
s32 amap_release_cluster(struct super_block *sb, u32 clu); /* Only update AMAP */
/* amap_smart.c : misc (for defrag) */
s32 amap_mark_ignore(struct super_block *sb, u32 clu);
s32 amap_unmark_ignore(struct super_block *sb, u32 clu);
s32 amap_unmark_ignore_all(struct super_block *sb);
s32 amap_check_working(struct super_block *sb, u32 clu);
s32 amap_get_freeclus(struct super_block *sb, u32 clu);
/* amap_smart.c : stat AU */
u32 amap_get_au_stat(struct super_block *sb, s32 mode);
/* blkdev.c */
s32 bdev_open_dev(struct super_block *sb);
s32 bdev_close_dev(struct super_block *sb);
s32 bdev_check_bdi_valid(struct super_block *sb);
s32 bdev_readahead(struct super_block *sb, u32 secno, u32 num_secs);
s32 bdev_mread(struct super_block *sb, u32 secno, struct buffer_head **bh, u32 num_secs, s32 read);
s32 bdev_mwrite(struct super_block *sb, u32 secno, struct buffer_head *bh, u32 num_secs, s32 sync);
s32 bdev_sync_all(struct super_block *sb);
/* blkdev.c : sector read/write functions */
s32 read_sect(struct super_block *sb, u32 sec, struct buffer_head **bh, s32 read);
s32 write_sect(struct super_block *sb, u32 sec, struct buffer_head *bh, s32 sync);
s32 read_msect(struct super_block *sb, u32 sec, struct buffer_head **bh, s32 num_secs, s32 read);
s32 write_msect(struct super_block *sb, u32 sec, struct buffer_head *bh, s32 num_secs, s32 sync);
s32 write_msect_zero(struct super_block *sb, u32 sec, s32 num_secs);
/* misc.c */
u8 calc_chksum_1byte(void *data, s32 len, u8 chksum);
u16 calc_chksum_2byte(void *data, s32 len, u16 chksum, s32 type);
/* extent.c */
s32 extent_cache_init(void);
void extent_cache_shutdown(void);
void extent_cache_init_inode(struct inode *inode);
void extent_cache_inval_inode(struct inode *inode);
s32 extent_get_clus(struct inode *inode, s32 cluster, s32 *fclus,
u32 *dclus, u32 *last_dclus, s32 allow_eof);
/*----------------------------------------------------------------------*/
/* Wrapper Function */
/*----------------------------------------------------------------------*/
void set_sb_dirty(struct super_block *sb);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _SDFAT_CORE_H */
/* end of core.h */

1533
fs/sdfat/core_exfat.c Normal file

File diff suppressed because it is too large Load Diff

1453
fs/sdfat/core_fat.c Normal file

File diff suppressed because it is too large Load Diff

1377
fs/sdfat/dfr.c Normal file

File diff suppressed because it is too large Load Diff

261
fs/sdfat/dfr.h Normal file
View File

@ -0,0 +1,261 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SDFAT_DEFRAG_H
#define _SDFAT_DEFRAG_H
#ifdef CONFIG_SDFAT_DFR
/* Tuning parameters */
#define DFR_MIN_TIMEOUT (1 * HZ) // Minimum timeout for forced-sync
#define DFR_DEFAULT_TIMEOUT (10 * HZ) // Default timeout for forced-sync
#define DFR_DEFAULT_CLEAN_RATIO (50) // Wake-up daemon when clean AU ratio under 50%
#define DFR_DEFAULT_WAKEUP_RATIO (10) // Wake-up daemon when clean AU ratio under 10%, regardless of frag_ratio
#define DFR_DEFAULT_FRAG_RATIO (130) // Wake-up daemon when frag_ratio over 130%
#define DFR_DEFAULT_PACKING_RATIO (10) // Call allocator with PACKING flag, when clean AU ratio under 10%
#define DFR_DEFAULT_STOP_RATIO (98) // Stop defrag_daemon when disk used ratio over 98%
#define DFR_FULL_RATIO (100)
#define DFR_MAX_AU_MOVED (16) // Maximum # of AUs for a request
/* Debugging support*/
#define dfr_err(fmt, args...) pr_err("DFR: " fmt "\n", args)
#ifdef CONFIG_SDFAT_DFR_DEBUG
#define dfr_debug(fmt, args...) pr_debug("DFR: " fmt "\n", args)
#else
#define dfr_debug(fmt, args...)
#endif
/* Error handling */
#define ERR_HANDLE(err) { \
if (err) { \
dfr_debug("err %d", err); \
goto error; \
} \
}
#define ERR_HANDLE2(cond, err, val) { \
if (cond) { \
err = val; \
dfr_debug("err %d", err); \
goto error; \
} \
}
/* Arguments IN-OUT */
#define IN
#define OUT
#define INOUT
/* Macros */
#define GET64_HI(var64) ((unsigned int)((var64) >> 32))
#define GET64_LO(var64) ((unsigned int)(((var64) << 32) >> 32))
#define SET64_HI(dst64, var32) { (dst64) = ((loff_t)(var32) << 32) | ((dst64) & 0x00000000ffffffffLL); }
#define SET64_LO(dst64, var32) { (dst64) = ((dst64) & 0xffffffff00000000LL) | ((var32) & 0x00000000ffffffffLL); }
#define GET32_HI(var32) ((unsigned short)((var32) >> 16))
#define GET32_LO(var32) ((unsigned short)(((var32) << 16) >> 16))
#define SET32_HI(dst32, var16) { (dst32) = ((unsigned int)(var16) << 16) | ((dst32) & 0x0000ffff); }
#define SET32_LO(dst32, var16) { (dst32) = ((dst32) & 0xffff0000) | ((unsigned int)(var16) & 0x0000ffff); }
/* FAT32 related */
#define FAT32_EOF (0x0fffffff)
#define FAT32_RESERVED (0x0ffffff7)
#define FAT32_UNUSED_CLUS (2)
#define CLUS_PER_AU(sb) ( \
(SDFAT_SB(sb)->options.amap_opt.sect_per_au) >> (SDFAT_SB(sb)->fsi.sect_per_clus_bits) \
)
#define PAGES_PER_AU(sb) ( \
((SDFAT_SB(sb)->options.amap_opt.sect_per_au) << ((sb)->s_blocksize_bits)) \
>> PAGE_SHIFT \
)
#define PAGES_PER_CLUS(sb) ((SDFAT_SB(sb)->fsi.cluster_size) >> PAGE_SHIFT)
#define FAT32_CHECK_CLUSTER(fsi, clus, err) \
{ \
if (((clus) < FAT32_UNUSED_CLUS) || \
((clus) > (fsi)->num_clusters) || \
((clus) >= FAT32_RESERVED)) { \
dfr_err("clus %08x, fsi->num_clusters %08x", (clus), (fsi)->num_clusters); \
err = -EINVAL; \
} else { \
err = 0; \
} \
}
/* IOCTL_DFR_INFO */
struct defrag_info_arg {
/* PBS info */
unsigned int sec_sz;
unsigned int clus_sz;
unsigned int total_sec;
unsigned int fat_offset_sec;
unsigned int fat_sz_sec;
unsigned int n_fat;
unsigned int hidden_sectors;
/* AU info */
unsigned int sec_per_au;
};
/* IOC_DFR_TRAV */
#define DFR_TRAV_HEADER_IDX (0)
#define DFR_TRAV_TYPE_HEADER (0x0000000F)
#define DFR_TRAV_TYPE_DIR (1)
#define DFR_TRAV_TYPE_FILE (2)
#define DFR_TRAV_TYPE_TEST (DFR_TRAV_TYPE_HEADER | 0x10000000)
#define DFR_TRAV_ROOT_IPOS (0xFFFFFFFFFFFFFFFFLL)
struct defrag_trav_arg {
int type;
unsigned int start_clus;
loff_t i_pos;
char name[MAX_DOSNAME_BUF_SIZE];
char dummy1;
int dummy2;
};
#define DFR_TRAV_STAT_DONE (0x1)
#define DFR_TRAV_STAT_MORE (0x2)
#define DFR_TRAV_STAT_ERR (0xFF)
struct defrag_trav_header {
int type;
unsigned int start_clus;
loff_t i_pos;
char name[MAX_DOSNAME_BUF_SIZE];
char stat;
unsigned int nr_entries;
};
/* IOC_DFR_REQ */
#define REQ_HEADER_IDX (0)
#define DFR_CHUNK_STAT_ERR (0xFFFFFFFF)
#define DFR_CHUNK_STAT_REQ (0x1)
#define DFR_CHUNK_STAT_WB (0x2)
#define DFR_CHUNK_STAT_FAT (0x4)
#define DFR_CHUNK_STAT_PREP (DFR_CHUNK_STAT_REQ | DFR_CHUNK_STAT_WB | DFR_CHUNK_STAT_FAT)
#define DFR_CHUNK_STAT_PASS (0x0000000F)
struct defrag_chunk_header {
int mode;
unsigned int nr_chunks;
loff_t dummy1;
int dummy2[4];
union {
int *dummy3;
int dummy4;
};
int dummy5;
};
struct defrag_chunk_info {
int stat;
/* File related */
unsigned int f_clus;
loff_t i_pos;
/* Cluster related */
unsigned int d_clus;
unsigned int nr_clus;
unsigned int prev_clus;
unsigned int next_clus;
union {
void *dummy;
/* req status */
unsigned int new_idx;
};
/* AU related */
unsigned int au_clus;
};
/* Global info */
#define DFR_MODE_BACKGROUND (0x1)
#define DFR_MODE_FOREGROUND (0x2)
#define DFR_MODE_ONESHOT (0x4)
#define DFR_MODE_BATCHED (0x8)
#define DFR_MODE_TEST (DFR_MODE_BACKGROUND | 0x10000000)
#define DFR_SB_STAT_IDLE (0)
#define DFR_SB_STAT_REQ (1)
#define DFR_SB_STAT_VALID (2)
#define DFR_INO_STAT_IDLE (0)
#define DFR_INO_STAT_REQ (1)
struct defrag_info {
struct mutex lock;
atomic_t stat;
struct defrag_chunk_info *chunks;
unsigned int nr_chunks;
struct list_head entry;
};
/* SPO test flags */
#define DFR_SPO_NONE (0)
#define DFR_SPO_NORMAL (1)
#define DFR_SPO_DISCARD (2)
#define DFR_SPO_FAT_NEXT (3)
#define DFR_SPO_RANDOM (4)
/* Extern functions */
int defrag_get_info(struct super_block *sb, struct defrag_info_arg *arg);
int defrag_scan_dir(struct super_block *sb, struct defrag_trav_arg *arg);
int defrag_validate_cluster(struct inode *inode, struct defrag_chunk_info *chunk, int skip_prev);
int defrag_reserve_clusters(struct super_block *sb, int nr_clus);
int defrag_mark_ignore(struct super_block *sb, unsigned int clus);
void defrag_unmark_ignore_all(struct super_block *sb);
int defrag_map_cluster(struct inode *inode, unsigned int clu_offset, unsigned int *clu);
void defrag_writepage_end_io(struct page *page);
void defrag_update_fat_prev(struct super_block *sb, int force);
void defrag_update_fat_next(struct super_block *sb);
void defrag_check_discard(struct super_block *sb);
int defrag_free_cluster(struct super_block *sb, unsigned int clus);
int defrag_check_defrag_required(struct super_block *sb, int *totalau, int *cleanau, int *fullau);
int defrag_check_defrag_on(struct inode *inode, loff_t start, loff_t end, int cancel, const char *caller);
#ifdef CONFIG_SDFAT_DFR_DEBUG
void defrag_spo_test(struct super_block *sb, int flag, const char *caller);
#endif
#endif /* CONFIG_SDFAT_DFR */
#endif /* _SDFAT_DEFRAG_H */

355
fs/sdfat/extent.c Normal file
View File

@ -0,0 +1,355 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* linux/fs/fat/cache.c
*
* Written 1992,1993 by Werner Almesberger
*
* Mar 1999. AV. Changed cache, so that it uses the starting cluster instead
* of inode number.
* May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers.
*/
/************************************************************************/
/* */
/* PROJECT : exFAT & FAT12/16/32 File System */
/* FILE : extent.c */
/* PURPOSE : Improve the performance of traversing fat chain. */
/* */
/*----------------------------------------------------------------------*/
/* NOTES */
/* */
/* */
/************************************************************************/
#include <linux/slab.h>
#include "sdfat.h"
#include "core.h"
#define EXTENT_CACHE_VALID 0
/* this must be > 0. */
#define EXTENT_MAX_CACHE 16
struct extent_cache {
struct list_head cache_list;
s32 nr_contig; /* number of contiguous clusters */
s32 fcluster; /* cluster number in the file. */
u32 dcluster; /* cluster number on disk. */
};
struct extent_cache_id {
u32 id;
s32 nr_contig;
s32 fcluster;
u32 dcluster;
};
static struct kmem_cache *extent_cache_cachep;
static void init_once(void *c)
{
struct extent_cache *cache = (struct extent_cache *)c;
INIT_LIST_HEAD(&cache->cache_list);
}
s32 extent_cache_init(void)
{
extent_cache_cachep = kmem_cache_create("sdfat_extent_cache",
sizeof(struct extent_cache),
0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
init_once);
if (!extent_cache_cachep)
return -ENOMEM;
return 0;
}
void extent_cache_shutdown(void)
{
if (!extent_cache_cachep)
return;
kmem_cache_destroy(extent_cache_cachep);
}
void extent_cache_init_inode(struct inode *inode)
{
EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent);
spin_lock_init(&extent->cache_lru_lock);
extent->nr_caches = 0;
extent->cache_valid_id = EXTENT_CACHE_VALID + 1;
INIT_LIST_HEAD(&extent->cache_lru);
}
static inline struct extent_cache *extent_cache_alloc(void)
{
return kmem_cache_alloc(extent_cache_cachep, GFP_NOFS);
}
static inline void extent_cache_free(struct extent_cache *cache)
{
BUG_ON(!list_empty(&cache->cache_list));
kmem_cache_free(extent_cache_cachep, cache);
}
static inline void extent_cache_update_lru(struct inode *inode,
struct extent_cache *cache)
{
EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent);
if (extent->cache_lru.next != &cache->cache_list)
list_move(&cache->cache_list, &extent->cache_lru);
}
static s32 extent_cache_lookup(struct inode *inode, s32 fclus,
struct extent_cache_id *cid,
s32 *cached_fclus, u32 *cached_dclus)
{
EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent);
static struct extent_cache nohit = { .fcluster = 0, };
struct extent_cache *hit = &nohit, *p;
s32 offset = -1;
spin_lock(&extent->cache_lru_lock);
list_for_each_entry(p, &extent->cache_lru, cache_list) {
/* Find the cache of "fclus" or nearest cache. */
if (p->fcluster <= fclus && hit->fcluster < p->fcluster) {
hit = p;
if ((hit->fcluster + hit->nr_contig) < fclus) {
offset = hit->nr_contig;
} else {
offset = fclus - hit->fcluster;
break;
}
}
}
if (hit != &nohit) {
extent_cache_update_lru(inode, hit);
cid->id = extent->cache_valid_id;
cid->nr_contig = hit->nr_contig;
cid->fcluster = hit->fcluster;
cid->dcluster = hit->dcluster;
*cached_fclus = cid->fcluster + offset;
*cached_dclus = cid->dcluster + offset;
}
spin_unlock(&extent->cache_lru_lock);
return offset;
}
static struct extent_cache *extent_cache_merge(struct inode *inode,
struct extent_cache_id *new)
{
EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent);
struct extent_cache *p;
list_for_each_entry(p, &extent->cache_lru, cache_list) {
/* Find the same part as "new" in cluster-chain. */
if (p->fcluster == new->fcluster) {
ASSERT(p->dcluster == new->dcluster);
if (new->nr_contig > p->nr_contig)
p->nr_contig = new->nr_contig;
return p;
}
}
return NULL;
}
static void extent_cache_add(struct inode *inode, struct extent_cache_id *new)
{
EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent);
struct extent_cache *cache, *tmp;
if (new->fcluster == -1) /* dummy cache */
return;
spin_lock(&extent->cache_lru_lock);
if (new->id != EXTENT_CACHE_VALID &&
new->id != extent->cache_valid_id)
goto out; /* this cache was invalidated */
cache = extent_cache_merge(inode, new);
if (cache == NULL) {
if (extent->nr_caches < EXTENT_MAX_CACHE) {
extent->nr_caches++;
spin_unlock(&extent->cache_lru_lock);
tmp = extent_cache_alloc();
if (!tmp) {
spin_lock(&extent->cache_lru_lock);
extent->nr_caches--;
spin_unlock(&extent->cache_lru_lock);
return;
}
spin_lock(&extent->cache_lru_lock);
cache = extent_cache_merge(inode, new);
if (cache != NULL) {
extent->nr_caches--;
extent_cache_free(tmp);
goto out_update_lru;
}
cache = tmp;
} else {
struct list_head *p = extent->cache_lru.prev;
cache = list_entry(p, struct extent_cache, cache_list);
}
cache->fcluster = new->fcluster;
cache->dcluster = new->dcluster;
cache->nr_contig = new->nr_contig;
}
out_update_lru:
extent_cache_update_lru(inode, cache);
out:
spin_unlock(&extent->cache_lru_lock);
}
/*
* Cache invalidation occurs rarely, thus the LRU chain is not updated. It
* fixes itself after a while.
*/
static void __extent_cache_inval_inode(struct inode *inode)
{
EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent);
struct extent_cache *cache;
while (!list_empty(&extent->cache_lru)) {
cache = list_entry(extent->cache_lru.next,
struct extent_cache, cache_list);
list_del_init(&cache->cache_list);
extent->nr_caches--;
extent_cache_free(cache);
}
/* Update. The copy of caches before this id is discarded. */
extent->cache_valid_id++;
if (extent->cache_valid_id == EXTENT_CACHE_VALID)
extent->cache_valid_id++;
}
void extent_cache_inval_inode(struct inode *inode)
{
EXTENT_T *extent = &(SDFAT_I(inode)->fid.extent);
spin_lock(&extent->cache_lru_lock);
__extent_cache_inval_inode(inode);
spin_unlock(&extent->cache_lru_lock);
}
static inline s32 cache_contiguous(struct extent_cache_id *cid, u32 dclus)
{
cid->nr_contig++;
return ((cid->dcluster + cid->nr_contig) == dclus);
}
static inline void cache_init(struct extent_cache_id *cid, s32 fclus, u32 dclus)
{
cid->id = EXTENT_CACHE_VALID;
cid->fcluster = fclus;
cid->dcluster = dclus;
cid->nr_contig = 0;
}
s32 extent_get_clus(struct inode *inode, s32 cluster, s32 *fclus,
u32 *dclus, u32 *last_dclus, s32 allow_eof)
{
struct super_block *sb = inode->i_sb;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
s32 limit = (s32)(fsi->num_clusters);
FILE_ID_T *fid = &(SDFAT_I(inode)->fid);
struct extent_cache_id cid;
u32 content;
/* FOR GRACEFUL ERROR HANDLING */
if (IS_CLUS_FREE(fid->start_clu)) {
sdfat_fs_error(sb, "invalid access to "
"extent cache (entry 0x%08x)", fid->start_clu);
ASSERT(0);
return -EIO;
}
/* We allow max clusters per a file upto max of signed integer */
if (fsi->num_clusters & 0x80000000)
limit = 0x7FFFFFFF;
*fclus = 0;
*dclus = fid->start_clu;
*last_dclus = *dclus;
/*
* Don`t use extent_cache if zero offset or non-cluster allocation
*/
if ((cluster == 0) || IS_CLUS_EOF(*dclus))
return 0;
cache_init(&cid, -1, -1);
if (extent_cache_lookup(inode, cluster, &cid, fclus, dclus) < 0) {
/*
* dummy, always not contiguous
* This is reinitialized by cache_init(), later.
*/
ASSERT((cid.id == EXTENT_CACHE_VALID)
&& (cid.fcluster == -1)
&& (cid.dcluster == -1)
&& (cid.nr_contig == 0));
}
if (*fclus == cluster)
return 0;
while (*fclus < cluster) {
/* prevent the infinite loop of cluster chain */
if (*fclus > limit) {
sdfat_fs_error(sb,
"%s: detected the cluster chain loop"
" (i_pos %d)", __func__,
(*fclus));
return -EIO;
}
if (fat_ent_get_safe(sb, *dclus, &content))
return -EIO;
*last_dclus = *dclus;
*dclus = content;
(*fclus)++;
if (IS_CLUS_EOF(content)) {
if (!allow_eof) {
sdfat_fs_error(sb,
"%s: invalid cluster chain (i_pos %d,"
"last_clus 0x%08x is EOF)",
__func__, *fclus, (*last_dclus));
return -EIO;
}
break;
}
if (!cache_contiguous(&cid, *dclus))
cache_init(&cid, *fclus, *dclus);
}
extent_cache_add(inode, &cid);
return 0;
}

412
fs/sdfat/fatent.c Normal file
View File

@ -0,0 +1,412 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/************************************************************************/
/* */
/* PROJECT : exFAT & FAT12/16/32 File System */
/* FILE : fatent.c */
/* PURPOSE : sdFAT FAT entry manager */
/* */
/*----------------------------------------------------------------------*/
/* NOTES */
/* */
/* */
/************************************************************************/
#include <asm/unaligned.h>
#include "sdfat.h"
#include "core.h"
/*----------------------------------------------------------------------*/
/* Global Variable Definitions */
/*----------------------------------------------------------------------*/
/* All buffer structures are protected w/ fsi->v_sem */
/*----------------------------------------------------------------------*/
/* Static functions */
/*----------------------------------------------------------------------*/
/*======================================================================*/
/* FAT Read/Write Functions */
/*======================================================================*/
/* in : sb, loc
* out: content
* returns 0 on success, -1 on error
*/
static s32 exfat_ent_get(struct super_block *sb, u32 loc, u32 *content)
{
u32 sec, off, _content;
u8 *fat_sector;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
/* fsi->vol_type == EXFAT */
sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2));
off = (loc << 2) & (u32)(sb->s_blocksize - 1);
fat_sector = fcache_getblk(sb, sec);
if (!fat_sector)
return -EIO;
_content = le32_to_cpu(*(__le32 *)(&fat_sector[off]));
/* remap reserved clusters to simplify code */
if (_content >= CLUSTER_32(0xFFFFFFF8))
_content = CLUS_EOF;
*content = CLUSTER_32(_content);
return 0;
}
static s32 exfat_ent_set(struct super_block *sb, u32 loc, u32 content)
{
u32 sec, off;
u8 *fat_sector;
__le32 *fat_entry;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2));
off = (loc << 2) & (u32)(sb->s_blocksize - 1);
fat_sector = fcache_getblk(sb, sec);
if (!fat_sector)
return -EIO;
fat_entry = (__le32 *)&(fat_sector[off]);
*fat_entry = cpu_to_le32(content);
return fcache_modify(sb, sec);
}
#define FATENT_FAT32_VALID_MASK (0x0FFFFFFFU)
#define FATENT_FAT32_IGNORE_MASK (0xF0000000U)
static s32 fat32_ent_get(struct super_block *sb, u32 loc, u32 *content)
{
u32 sec, off, _content;
u8 *fat_sector;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2));
off = (loc << 2) & (u32)(sb->s_blocksize - 1);
fat_sector = fcache_getblk(sb, sec);
if (!fat_sector)
return -EIO;
_content = le32_to_cpu(*(__le32 *)(&fat_sector[off]));
_content &= FATENT_FAT32_VALID_MASK;
/* remap reserved clusters to simplify code */
if (_content == CLUSTER_32(0x0FFFFFF7U))
_content = CLUS_BAD;
else if (_content >= CLUSTER_32(0x0FFFFFF8U))
_content = CLUS_EOF;
*content = CLUSTER_32(_content);
return 0;
}
static s32 fat32_ent_set(struct super_block *sb, u32 loc, u32 content)
{
u32 sec, off;
u8 *fat_sector;
__le32 *fat_entry;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
content &= FATENT_FAT32_VALID_MASK;
sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2));
off = (loc << 2) & (u32)(sb->s_blocksize - 1);
fat_sector = fcache_getblk(sb, sec);
if (!fat_sector)
return -EIO;
fat_entry = (__le32 *)&(fat_sector[off]);
content |= (le32_to_cpu(*fat_entry) & FATENT_FAT32_IGNORE_MASK);
*fat_entry = cpu_to_le32(content);
return fcache_modify(sb, sec);
}
#define FATENT_FAT16_VALID_MASK (0x0000FFFFU)
static s32 fat16_ent_get(struct super_block *sb, u32 loc, u32 *content)
{
u32 sec, off, _content;
u8 *fat_sector;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-1));
off = (loc << 1) & (u32)(sb->s_blocksize - 1);
fat_sector = fcache_getblk(sb, sec);
if (!fat_sector)
return -EIO;
_content = (u32)le16_to_cpu(*(__le16 *)(&fat_sector[off]));
_content &= FATENT_FAT16_VALID_MASK;
/* remap reserved clusters to simplify code */
if (_content == CLUSTER_16(0xFFF7U))
_content = CLUS_BAD;
else if (_content >= CLUSTER_16(0xFFF8U))
_content = CLUS_EOF;
*content = CLUSTER_32(_content);
return 0;
}
static s32 fat16_ent_set(struct super_block *sb, u32 loc, u32 content)
{
u32 sec, off;
u8 *fat_sector;
__le16 *fat_entry;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
content &= FATENT_FAT16_VALID_MASK;
sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-1));
off = (loc << 1) & (u32)(sb->s_blocksize - 1);
fat_sector = fcache_getblk(sb, sec);
if (!fat_sector)
return -EIO;
fat_entry = (__le16 *)&(fat_sector[off]);
*fat_entry = cpu_to_le16(content);
return fcache_modify(sb, sec);
}
#define FATENT_FAT12_VALID_MASK (0x00000FFFU)
static s32 fat12_ent_get(struct super_block *sb, u32 loc, u32 *content)
{
u32 sec, off, _content;
u8 *fat_sector;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
sec = fsi->FAT1_start_sector + ((loc + (loc >> 1)) >> sb->s_blocksize_bits);
off = (loc + (loc >> 1)) & (u32)(sb->s_blocksize - 1);
fat_sector = fcache_getblk(sb, sec);
if (!fat_sector)
return -EIO;
if (off == (u32)(sb->s_blocksize - 1)) {
_content = (u32) fat_sector[off];
fat_sector = fcache_getblk(sb, ++sec);
if (!fat_sector)
return -EIO;
_content |= (u32) fat_sector[0] << 8;
} else {
_content = get_unaligned_le16(&fat_sector[off]);
}
if (loc & 1)
_content >>= 4;
_content &= FATENT_FAT12_VALID_MASK;
/* remap reserved clusters to simplify code */
if (_content == CLUSTER_16(0x0FF7U))
_content = CLUS_BAD;
else if (_content >= CLUSTER_16(0x0FF8U))
_content = CLUS_EOF;
*content = CLUSTER_32(_content);
return 0;
}
static s32 fat12_ent_set(struct super_block *sb, u32 loc, u32 content)
{
u32 sec, off;
u8 *fat_sector, *fat_entry;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
content &= FATENT_FAT12_VALID_MASK;
sec = fsi->FAT1_start_sector + ((loc + (loc >> 1)) >> sb->s_blocksize_bits);
off = (loc + (loc >> 1)) & (u32)(sb->s_blocksize - 1);
fat_sector = fcache_getblk(sb, sec);
if (!fat_sector)
return -EIO;
if (loc & 1) { /* odd */
content <<= 4;
if (off == (u32)(sb->s_blocksize-1)) {
fat_sector[off] = (u8)(content | (fat_sector[off] & 0x0F));
if (fcache_modify(sb, sec))
return -EIO;
fat_sector = fcache_getblk(sb, ++sec);
if (!fat_sector)
return -EIO;
fat_sector[0] = (u8)(content >> 8);
} else {
fat_entry = &(fat_sector[off]);
content |= 0x000F & get_unaligned_le16(fat_entry);
put_unaligned_le16(content, fat_entry);
}
} else { /* even */
fat_sector[off] = (u8)(content);
if (off == (u32)(sb->s_blocksize-1)) {
fat_sector[off] = (u8)(content);
if (fcache_modify(sb, sec))
return -EIO;
fat_sector = fcache_getblk(sb, ++sec);
if (!fat_sector)
return -EIO;
fat_sector[0] = (u8)((fat_sector[0] & 0xF0) | (content >> 8));
} else {
fat_entry = &(fat_sector[off]);
content |= 0xF000 & get_unaligned_le16(fat_entry);
put_unaligned_le16(content, fat_entry);
}
}
return fcache_modify(sb, sec);
}
static FATENT_OPS_T fat12_ent_ops = {
fat12_ent_get,
fat12_ent_set
};
static FATENT_OPS_T fat16_ent_ops = {
fat16_ent_get,
fat16_ent_set
};
static FATENT_OPS_T fat32_ent_ops = {
fat32_ent_get,
fat32_ent_set
};
static FATENT_OPS_T exfat_ent_ops = {
exfat_ent_get,
exfat_ent_set
};
s32 fat_ent_ops_init(struct super_block *sb)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
switch (fsi->vol_type) {
case EXFAT:
fsi->fatent_ops = &exfat_ent_ops;
break;
case FAT32:
fsi->fatent_ops = &fat32_ent_ops;
break;
case FAT16:
fsi->fatent_ops = &fat16_ent_ops;
break;
case FAT12:
fsi->fatent_ops = &fat12_ent_ops;
break;
default:
fsi->fatent_ops = NULL;
EMSG("Unknown volume type : %d", (int)fsi->vol_type);
return -ENOTSUPP;
}
return 0;
}
static inline bool is_reserved_clus(u32 clus)
{
if (IS_CLUS_FREE(clus))
return true;
if (IS_CLUS_EOF(clus))
return true;
if (IS_CLUS_BAD(clus))
return true;
return false;
}
static inline bool is_valid_clus(FS_INFO_T *fsi, u32 clus)
{
if (clus < CLUS_BASE || fsi->num_clusters <= clus)
return false;
return true;
}
s32 fat_ent_get(struct super_block *sb, u32 loc, u32 *content)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
s32 err;
if (!is_valid_clus(fsi, loc)) {
sdfat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", loc);
return -EIO;
}
err = fsi->fatent_ops->ent_get(sb, loc, content);
if (err) {
sdfat_fs_error(sb, "failed to access to FAT "
"(entry 0x%08x, err:%d)", loc, err);
return err;
}
if (!is_reserved_clus(*content) && !is_valid_clus(fsi, *content)) {
sdfat_fs_error(sb, "invalid access to FAT (entry 0x%08x) "
"bogus content (0x%08x)", loc, *content);
return -EIO;
}
return 0;
}
s32 fat_ent_set(struct super_block *sb, u32 loc, u32 content)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
return fsi->fatent_ops->ent_set(sb, loc, content);
}
s32 fat_ent_get_safe(struct super_block *sb, u32 loc, u32 *content)
{
s32 err = fat_ent_get(sb, loc, content);
if (err)
return err;
if (IS_CLUS_FREE(*content)) {
sdfat_fs_error(sb, "invalid access to FAT free cluster "
"(entry 0x%08x)", loc);
return -EIO;
}
if (IS_CLUS_BAD(*content)) {
sdfat_fs_error(sb, "invalid access to FAT bad cluster "
"(entry 0x%08x)", loc);
return -EIO;
}
return 0;
}
/* end of fatent.c */

380
fs/sdfat/misc.c Normal file
View File

@ -0,0 +1,380 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* linux/fs/fat/misc.c
*
* Written 1992,1993 by Werner Almesberger
* 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
* and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
*/
/************************************************************************/
/* */
/* PROJECT : exFAT & FAT12/16/32 File System */
/* FILE : misc.c */
/* PURPOSE : Helper function for checksum and handing sdFAT error */
/* */
/*----------------------------------------------------------------------*/
/* NOTES */
/* */
/* */
/************************************************************************/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/buffer_head.h>
#include <linux/time.h>
#include "sdfat.h"
#include "version.h"
#ifdef CONFIG_SDFAT_SUPPORT_STLOG
#include <linux/stlog.h>
#else
#define ST_LOG(fmt, ...)
#endif
/*
* sdfat_fs_error reports a file system problem that might indicate fa data
* corruption/inconsistency. Depending on 'errors' mount option the
* panic() is called, or error message is printed FAT and nothing is done,
* or filesystem is remounted read-only (default behavior).
* In case the file system is remounted read-only, it can be made writable
* again by remounting it.
*/
void __sdfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
{
struct sdfat_mount_options *opts = &SDFAT_SB(sb)->options;
va_list args;
struct va_format vaf;
struct block_device *bdev = sb->s_bdev;
dev_t bd_dev = bdev ? bdev->bd_dev : 0;
if (report) {
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
pr_err("[SDFAT](%s[%d:%d]):ERR: %pV\n",
sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf);
#ifdef CONFIG_SDFAT_SUPPORT_STLOG
if (opts->errors == SDFAT_ERRORS_RO && !(sb->s_flags & MS_RDONLY)) {
ST_LOG("[SDFAT](%s[%d:%d]):ERR: %pV\n",
sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf);
}
#endif
va_end(args);
}
if (opts->errors == SDFAT_ERRORS_PANIC) {
panic("[SDFAT](%s[%d:%d]): fs panic from previous error\n",
sb->s_id, MAJOR(bd_dev), MINOR(bd_dev));
} else if (opts->errors == SDFAT_ERRORS_RO && !(sb->s_flags & MS_RDONLY)) {
sb->s_flags |= MS_RDONLY;
pr_err("[SDFAT](%s[%d:%d]): Filesystem has been set "
"read-only\n", sb->s_id, MAJOR(bd_dev), MINOR(bd_dev));
#ifdef CONFIG_SDFAT_SUPPORT_STLOG
ST_LOG("[SDFAT](%s[%d:%d]): Filesystem has been set read-only\n",
sb->s_id, MAJOR(bd_dev), MINOR(bd_dev));
#endif
}
}
EXPORT_SYMBOL(__sdfat_fs_error);
/**
* __sdfat_msg() - print preformated SDFAT specific messages.
* All logs except what uses sdfat_fs_error() should be written by __sdfat_msg()
* If 'st' is set, the log is propagated to ST_LOG.
*/
void __sdfat_msg(struct super_block *sb, const char *level, int st, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
struct block_device *bdev = sb->s_bdev;
dev_t bd_dev = bdev ? bdev->bd_dev : 0;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
/* level means KERN_ pacility level */
printk("%s[SDFAT](%s[%d:%d]): %pV\n", level,
sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf);
#ifdef CONFIG_SDFAT_SUPPORT_STLOG
if (st) {
ST_LOG("[SDFAT](%s[%d:%d]): %pV\n",
sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf);
}
#endif
va_end(args);
}
EXPORT_SYMBOL(__sdfat_msg);
void sdfat_log_version(void)
{
pr_info("[SDFAT] Filesystem version %s\n", SDFAT_VERSION);
#ifdef CONFIG_SDFAT_SUPPORT_STLOG
ST_LOG("[SDFAT] Filesystem version %s\n", SDFAT_VERSION);
#endif
}
EXPORT_SYMBOL(sdfat_log_version);
/* <linux/time.h> externs sys_tz
* extern struct timezone sys_tz;
*/
#define UNIX_SECS_1980 315532800L
#if BITS_PER_LONG == 64
#define UNIX_SECS_2108 4354819200L
#endif
/* days between 1970/01/01 and 1980/01/01 (2 leap days) */
#define DAYS_DELTA_DECADE (365 * 10 + 2)
/* 120 (2100 - 1980) isn't leap year */
#define NO_LEAP_YEAR_2100 (120)
#define IS_LEAP_YEAR(y) (!((y) & 0x3) && (y) != NO_LEAP_YEAR_2100)
#define SECS_PER_MIN (60)
#define SECS_PER_HOUR (60 * SECS_PER_MIN)
#define SECS_PER_DAY (24 * SECS_PER_HOUR)
#define MAKE_LEAP_YEAR(leap_year, year) \
do { \
/* 2100 isn't leap year */ \
if (unlikely(year > NO_LEAP_YEAR_2100)) \
leap_year = ((year + 3) / 4) - 1; \
else \
leap_year = ((year + 3) / 4); \
} while (0)
/* Linear day numbers of the respective 1sts in non-leap years. */
static time_t accum_days_in_year[] = {
/* Month : N 01 02 03 04 05 06 07 08 09 10 11 12 */
0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0,
};
/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
void sdfat_time_fat2unix(struct sdfat_sb_info *sbi, struct timespec *ts,
DATE_TIME_T *tp)
{
time_t year = tp->Year;
time_t ld; /* leap day */
MAKE_LEAP_YEAR(ld, year);
if (IS_LEAP_YEAR(year) && (tp->Month) > 2)
ld++;
ts->tv_sec = tp->Second + tp->Minute * SECS_PER_MIN
+ tp->Hour * SECS_PER_HOUR
+ (year * 365 + ld + accum_days_in_year[tp->Month]
+ (tp->Day - 1) + DAYS_DELTA_DECADE) * SECS_PER_DAY;
if (!sbi->options.tz_utc)
ts->tv_sec += sys_tz.tz_minuteswest * SECS_PER_MIN;
ts->tv_nsec = 0;
}
/* Convert linear UNIX date to a FAT time/date pair. */
void sdfat_time_unix2fat(struct sdfat_sb_info *sbi, struct timespec *ts,
DATE_TIME_T *tp)
{
time_t second = ts->tv_sec;
time_t day, month, year;
time_t ld; /* leap day */
if (!sbi->options.tz_utc)
second -= sys_tz.tz_minuteswest * SECS_PER_MIN;
/* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
if (second < UNIX_SECS_1980) {
tp->Second = 0;
tp->Minute = 0;
tp->Hour = 0;
tp->Day = 1;
tp->Month = 1;
tp->Year = 0;
return;
}
#if (BITS_PER_LONG == 64)
if (second >= UNIX_SECS_2108) {
tp->Second = 59;
tp->Minute = 59;
tp->Hour = 23;
tp->Day = 31;
tp->Month = 12;
tp->Year = 127;
return;
}
#endif
day = second / SECS_PER_DAY - DAYS_DELTA_DECADE;
year = day / 365;
MAKE_LEAP_YEAR(ld, year);
if (year * 365 + ld > day)
year--;
MAKE_LEAP_YEAR(ld, year);
day -= year * 365 + ld;
if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) {
month = 2;
} else {
if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3])
day--;
for (month = 1; month < 12; month++) {
if (accum_days_in_year[month + 1] > day)
break;
}
}
day -= accum_days_in_year[month];
tp->Second = second % SECS_PER_MIN;
tp->Minute = (second / SECS_PER_MIN) % 60;
tp->Hour = (second / SECS_PER_HOUR) % 24;
tp->Day = day + 1;
tp->Month = month;
tp->Year = year;
}
TIMESTAMP_T *tm_now(struct sdfat_sb_info *sbi, TIMESTAMP_T *tp)
{
struct timespec ts = CURRENT_TIME_SEC;
DATE_TIME_T dt;
sdfat_time_unix2fat(sbi, &ts, &dt);
tp->year = dt.Year;
tp->mon = dt.Month;
tp->day = dt.Day;
tp->hour = dt.Hour;
tp->min = dt.Minute;
tp->sec = dt.Second;
return tp;
}
u8 calc_chksum_1byte(void *data, s32 len, u8 chksum)
{
s32 i;
u8 *c = (u8 *) data;
for (i = 0; i < len; i++, c++)
chksum = (((chksum & 1) << 7) | ((chksum & 0xFE) >> 1)) + *c;
return chksum;
}
u16 calc_chksum_2byte(void *data, s32 len, u16 chksum, s32 type)
{
s32 i;
u8 *c = (u8 *) data;
for (i = 0; i < len; i++, c++) {
if (((i == 2) || (i == 3)) && (type == CS_DIR_ENTRY))
continue;
chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (u16) *c;
}
return chksum;
}
#ifdef CONFIG_SDFAT_TRACE_ELAPSED_TIME
struct timeval __t1, __t2;
u32 sdfat_time_current_usec(struct timeval *tv)
{
do_gettimeofday(tv);
return (u32)(tv->tv_sec*1000000 + tv->tv_usec);
}
#endif /* CONFIG_SDFAT_TRACE_ELAPSED_TIME */
#ifdef CONFIG_SDFAT_DBG_CAREFUL
/* Check the consistency of i_size_ondisk (FAT32, or flags 0x01 only) */
void sdfat_debug_check_clusters(struct inode *inode)
{
int num_clusters;
volatile uint32_t tmp_fat_chain[50];
volatile int num_clusters_org, tmp_i = 0;
CHAIN_T clu;
FILE_ID_T *fid = &(SDFAT_I(inode)->fid);
FS_INFO_T *fsi = &(SDFAT_SB(inode->i_sb)->fsi);
if (SDFAT_I(inode)->i_size_ondisk == 0)
num_clusters = 0;
else
num_clusters = (s32)((SDFAT_I(inode)->i_size_ondisk-1) >> fsi->cluster_size_bits) + 1;
clu.dir = fid->start_clu;
clu.size = num_clusters;
clu.flags = fid->flags;
num_clusters_org = num_clusters;
if (clu.flags == 0x03)
return;
while (num_clusters > 0) {
/* FAT chain logging */
tmp_fat_chain[tmp_i] = clu.dir;
tmp_i++;
if (tmp_i >= 50)
tmp_i = 0;
BUG_ON(IS_CLUS_EOF(clu.dir) || IS_CLUS_FREE(clu.dir));
if (get_next_clus_safe(inode->i_sb, &(clu.dir)))
EMSG("%s: failed to access to FAT\n");
num_clusters--;
}
BUG_ON(!IS_CLUS_EOF(clu.dir));
}
#endif /* CONFIG_SDFAT_DBG_CAREFUL */
#ifdef CONFIG_SDFAT_DBG_MSG
void __sdfat_dmsg(int level, const char *fmt, ...)
{
#ifdef CONFIG_SDFAT_DBG_SHOW_PID
struct va_format vaf;
va_list args;
/* should check type */
if (level > SDFAT_MSG_LEVEL)
return;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
/* fmt already includes KERN_ pacility level */
printk("[%u] %pV", current->pid, &vaf);
va_end(args);
#else
va_list args;
/* should check type */
if (level > SDFAT_MSG_LEVEL)
return;
va_start(args, fmt);
/* fmt already includes KERN_ pacility level */
vprintk(fmt, args);
va_end(args);
#endif
}
#endif

607
fs/sdfat/mpage.c Normal file
View File

@ -0,0 +1,607 @@
/*
* fs/mpage.c
*
* Copyright (C) 2002, Linus Torvalds.
*
* Contains functions related to preparing and submitting BIOs which contain
* multiple pagecache pages.
*
* 15May2002 Andrew Morton
* Initial version
* 27Jun2002 axboe@suse.de
* use bio_add_page() to build bio's just the right size
*/
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/************************************************************************/
/* */
/* PROJECT : exFAT & FAT12/16/32 File System */
/* FILE : core.c */
/* PURPOSE : sdFAT glue layer for supporting VFS */
/* */
/*----------------------------------------------------------------------*/
/* NOTES */
/* */
/* */
/************************************************************************/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/time.h>
#include <linux/buffer_head.h>
#include <linux/exportfs.h>
#include <linux/mount.h>
#include <linux/vfs.h>
#include <linux/parser.h>
#include <linux/uio.h>
#include <linux/writeback.h>
#include <linux/log2.h>
#include <linux/hash.h>
#include <linux/backing-dev.h>
#include <linux/sched.h>
#include <linux/fs_struct.h>
#include <linux/namei.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/swap.h> /* for mark_page_accessed() */
#include <asm/current.h>
#include <asm/unaligned.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
#include <linux/aio.h>
#endif
#include "sdfat.h"
#ifdef CONFIG_SDFAT_ALIGNED_MPAGE_WRITE
/*************************************************************************
* INNER FUNCTIONS FOR FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY
*************************************************************************/
static void __mpage_write_end_io(struct bio *bio, int err);
/*************************************************************************
* FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY
*************************************************************************/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
static inline void __sdfat_submit_bio_write2(int flags, struct bio *bio)
{
bio_set_op_attrs(bio, REQ_OP_WRITE, flags);
submit_bio(bio);
}
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0) */
static inline void __sdfat_submit_bio_write2(int flags, struct bio *bio)
{
submit_bio(WRITE | flags, bio);
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
static void mpage_write_end_io(struct bio *bio)
{
__mpage_write_end_io(bio, bio->bi_error);
}
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4,3,0) */
static void mpage_write_end_io(struct bio *bio, int err)
{
if (test_bit(BIO_UPTODATE, &bio->bi_flags))
err = 0;
__mpage_write_end_io(bio, err);
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
static inline int bio_get_nr_vecs(struct block_device *bdev)
{
return BIO_MAX_PAGES;
}
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0) */
/* EMPTY */
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
static inline sector_t __sdfat_bio_sector(struct bio *bio)
{
return bio->bi_iter.bi_sector;
}
static inline void __sdfat_set_bio_sector(struct bio *bio, sector_t sector)
{
bio->bi_iter.bi_sector = sector;
}
static inline unsigned int __sdfat_bio_size(struct bio *bio)
{
return bio->bi_iter.bi_size;
}
static inline void __sdfat_set_bio_size(struct bio *bio, unsigned int size)
{
bio->bi_iter.bi_size = size;
}
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) */
static inline sector_t __sdfat_bio_sector(struct bio *bio)
{
return bio->bi_sector;
}
static inline void __sdfat_set_bio_sector(struct bio *bio, sector_t sector)
{
bio->bi_sector = sector;
}
static inline unsigned int __sdfat_bio_size(struct bio *bio)
{
return bio->bi_size;
}
static inline void __sdfat_set_bio_size(struct bio *bio, unsigned int size)
{
bio->bi_size = size;
}
#endif
/* __check_dfr_on() and __dfr_writepage_end_io() functions
* are copied from sdfat.c
* Each function should be same perfectly
*/
static inline int __check_dfr_on(struct inode *inode, loff_t start, loff_t end, const char *fname)
{
#ifdef CONFIG_SDFAT_DFR
struct defrag_info *ino_dfr = &(SDFAT_I(inode)->dfr_info);
if ((atomic_read(&ino_dfr->stat) == DFR_INO_STAT_REQ) &&
fsapi_dfr_check_dfr_on(inode, start, end, 0, fname))
return 1;
#endif
return 0;
}
static inline int __dfr_writepage_end_io(struct page *page)
{
#ifdef CONFIG_SDFAT_DFR
struct defrag_info *ino_dfr = &(SDFAT_I(page->mapping->host)->dfr_info);
if (atomic_read(&ino_dfr->stat) == DFR_INO_STAT_REQ)
fsapi_dfr_writepage_endio(page);
#endif
return 0;
}
static inline unsigned int __calc_size_to_align(struct super_block *sb)
{
struct block_device *bdev = sb->s_bdev;
struct gendisk *disk;
struct request_queue *queue;
struct queue_limits *limit;
unsigned int max_sectors;
unsigned int aligned = 0;
disk = bdev->bd_disk;
if (!disk)
goto out;
queue = disk->queue;
if (!queue)
goto out;
limit = &queue->limits;
max_sectors = limit->max_sectors;
aligned = 1 << ilog2(max_sectors);
if (aligned && (max_sectors & (aligned - 1)))
aligned = 0;
out:
return aligned;
}
struct mpage_data {
struct bio *bio;
sector_t last_block_in_bio;
get_block_t *get_block;
unsigned int use_writepage;
unsigned int size_to_align;
};
/*
* I/O completion handler for multipage BIOs.
*
* The mpage code never puts partial pages into a BIO (except for end-of-file).
* If a page does not map to a contiguous run of blocks then it simply falls
* back to block_read_full_page().
*
* Why is this? If a page's completion depends on a number of different BIOs
* which can complete in any order (or at the same time) then determining the
* status of that page is hard. See end_buffer_async_read() for the details.
* There is no point in duplicating all that complexity.
*/
static void __mpage_write_end_io(struct bio *bio, int err)
{
struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
ASSERT(bio_data_dir(bio) == WRITE); /* only write */
do {
struct page *page = bvec->bv_page;
if (--bvec >= bio->bi_io_vec)
prefetchw(&bvec->bv_page->flags);
if (err) {
SetPageError(page);
if (page->mapping)
mapping_set_error(page->mapping, err);
}
__dfr_writepage_end_io(page);
end_page_writeback(page);
} while (bvec >= bio->bi_io_vec);
bio_put(bio);
}
static struct bio *mpage_bio_submit_write(int flags, struct bio *bio)
{
bio->bi_end_io = mpage_write_end_io;
__sdfat_submit_bio_write2(flags, bio);
return NULL;
}
static struct bio *
mpage_alloc(struct block_device *bdev,
sector_t first_sector, int nr_vecs,
gfp_t gfp_flags)
{
struct bio *bio;
bio = bio_alloc(gfp_flags, nr_vecs);
if (bio == NULL && (current->flags & PF_MEMALLOC)) {
while (!bio && (nr_vecs /= 2))
bio = bio_alloc(gfp_flags, nr_vecs);
}
if (bio) {
bio->bi_bdev = bdev;
__sdfat_set_bio_sector(bio, first_sector);
}
return bio;
}
static int sdfat_mpage_writepage(struct page *page,
struct writeback_control *wbc, void *data)
{
struct mpage_data *mpd = data;
struct bio *bio = mpd->bio;
struct address_space *mapping = page->mapping;
struct inode *inode = page->mapping->host;
const unsigned int blkbits = inode->i_blkbits;
const unsigned int blocks_per_page = PAGE_SIZE >> blkbits;
sector_t last_block;
sector_t block_in_file;
sector_t blocks[MAX_BUF_PER_PAGE];
unsigned int page_block;
unsigned int first_unmapped = blocks_per_page;
struct block_device *bdev = NULL;
int boundary = 0;
sector_t boundary_block = 0;
struct block_device *boundary_bdev = NULL;
int length;
struct buffer_head map_bh;
loff_t i_size = i_size_read(inode);
unsigned long end_index = i_size >> PAGE_SHIFT;
int ret = 0;
if (page_has_buffers(page)) {
struct buffer_head *head = page_buffers(page);
struct buffer_head *bh = head;
/* If they're all mapped and dirty, do it */
page_block = 0;
do {
BUG_ON(buffer_locked(bh));
if (!buffer_mapped(bh)) {
/*
* unmapped dirty buffers are created by
* __set_page_dirty_buffers -> mmapped data
*/
if (buffer_dirty(bh))
goto confused;
if (first_unmapped == blocks_per_page)
first_unmapped = page_block;
continue;
}
if (first_unmapped != blocks_per_page)
goto confused; /* hole -> non-hole */
if (!buffer_dirty(bh) || !buffer_uptodate(bh))
goto confused;
/* bh should be mapped if delay is set */
if (buffer_delay(bh)) {
sector_t blk_in_file =
(sector_t)(page->index << (PAGE_SHIFT - blkbits)) + page_block;
BUG_ON(bh->b_size != (1 << blkbits));
if (page->index > end_index) {
MMSG("%s(inode:%p) "
"over end with delayed buffer"
"(page_idx:%u, end_idx:%u)\n",
__func__, inode,
(u32)page->index,
(u32)end_index);
goto confused;
}
ret = mpd->get_block(inode, blk_in_file, bh, 1);
if (ret) {
MMSG("%s(inode:%p) "
"failed to getblk(ret:%d)\n",
__func__, inode, ret);
goto confused;
}
BUG_ON(buffer_delay(bh));
if (buffer_new(bh)) {
clear_buffer_new(bh);
unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr);
}
}
if (page_block) {
if (bh->b_blocknr != blocks[page_block-1] + 1) {
MMSG("%s(inode:%p) pblk(%d) "
"no_seq(prev:%lld, new:%lld)\n",
__func__, inode, page_block,
(u64)blocks[page_block-1],
(u64)bh->b_blocknr);
goto confused;
}
}
blocks[page_block++] = bh->b_blocknr;
boundary = buffer_boundary(bh);
if (boundary) {
boundary_block = bh->b_blocknr;
boundary_bdev = bh->b_bdev;
}
bdev = bh->b_bdev;
} while ((bh = bh->b_this_page) != head);
if (first_unmapped)
goto page_is_mapped;
/*
* Page has buffers, but they are all unmapped. The page was
* created by pagein or read over a hole which was handled by
* block_read_full_page(). If this address_space is also
* using mpage_readpages then this can rarely happen.
*/
goto confused;
}
/*
* The page has no buffers: map it to disk
*/
BUG_ON(!PageUptodate(page));
block_in_file = (sector_t)page->index << (PAGE_SHIFT - blkbits);
last_block = (i_size - 1) >> blkbits;
map_bh.b_page = page;
for (page_block = 0; page_block < blocks_per_page; ) {
map_bh.b_state = 0;
map_bh.b_size = 1 << blkbits;
if (mpd->get_block(inode, block_in_file, &map_bh, 1))
goto confused;
if (buffer_new(&map_bh))
unmap_underlying_metadata(map_bh.b_bdev,
map_bh.b_blocknr);
if (buffer_boundary(&map_bh)) {
boundary_block = map_bh.b_blocknr;
boundary_bdev = map_bh.b_bdev;
}
if (page_block) {
if (map_bh.b_blocknr != blocks[page_block-1] + 1)
goto confused;
}
blocks[page_block++] = map_bh.b_blocknr;
boundary = buffer_boundary(&map_bh);
bdev = map_bh.b_bdev;
if (block_in_file == last_block)
break;
block_in_file++;
}
BUG_ON(page_block == 0);
first_unmapped = page_block;
page_is_mapped:
if (page->index >= end_index) {
/*
* The page straddles i_size. It must be zeroed out on each
* and every writepage invocation because it may be mmapped.
* "A file is mapped in multiples of the page size. For a file
* that is not a multiple of the page size, the remaining memory
* is zeroed when mapped, and writes to that region are not
* written out to the file."
*/
unsigned int offset = i_size & (PAGE_SIZE - 1);
if (page->index > end_index || !offset) {
MMSG("%s(inode:%p) over end "
"(page_idx:%u, end_idx:%u off:%u)\n",
__func__, inode, (u32)page->index,
(u32)end_index, (u32)offset);
goto confused;
}
zero_user_segment(page, offset, PAGE_SIZE);
}
/*
* This page will go to BIO. Do we need to send this BIO off first?
*
* REMARK : added ELSE_IF for ALIGNMENT_MPAGE_WRITE of SDFAT
*/
if (bio) {
if (mpd->last_block_in_bio != blocks[0] - 1) {
bio = mpage_bio_submit_write(0, bio);
} else if (mpd->size_to_align) {
unsigned int mask = mpd->size_to_align - 1;
sector_t max_end_block =
(__sdfat_bio_sector(bio) & ~(mask)) + mask;
if ((__sdfat_bio_size(bio) != (1 << (mask + 1))) &&
(mpd->last_block_in_bio == max_end_block)) {
MMSG("%s(inode:%p) alignment mpage_bio_submit"
"(start:%u, len:%u aligned:%u)\n",
__func__, inode,
(unsigned int)__sdfat_bio_sector(bio),
(unsigned int)(mpd->last_block_in_bio -
__sdfat_bio_sector(bio) + 1),
(unsigned int)mpd->size_to_align);
bio = mpage_bio_submit_write(REQ_NOMERGE, bio);
}
}
}
alloc_new:
if (!bio) {
bio = mpage_alloc(bdev, blocks[0] << (blkbits - 9),
bio_get_nr_vecs(bdev), GFP_NOFS|__GFP_HIGH);
if (!bio)
goto confused;
}
/*
* Must try to add the page before marking the buffer clean or
* the confused fail path above (OOM) will be very confused when
* it finds all bh marked clean (i.e. it will not write anything)
*/
length = first_unmapped << blkbits;
if (bio_add_page(bio, page, length, 0) < length) {
bio = mpage_bio_submit_write(0, bio);
goto alloc_new;
}
/*
* OK, we have our BIO, so we can now mark the buffers clean. Make
* sure to only clean buffers which we know we'll be writing.
*/
if (page_has_buffers(page)) {
struct buffer_head *head = page_buffers(page);
struct buffer_head *bh = head;
unsigned int buffer_counter = 0;
do {
if (buffer_counter++ == first_unmapped)
break;
clear_buffer_dirty(bh);
bh = bh->b_this_page;
} while (bh != head);
/*
* we cannot drop the bh if the page is not uptodate
* or a concurrent readpage would fail to serialize with the bh
* and it would read from disk before we reach the platter.
*/
if (buffer_heads_over_limit && PageUptodate(page))
try_to_free_buffers(page);
}
BUG_ON(PageWriteback(page));
set_page_writeback(page);
/*
* FIXME FOR DEFRAGMENTATION : CODE REVIEW IS REQUIRED
*
* Turn off MAPPED flag in victim's bh if defrag on.
* Another write_begin can starts after get_block for defrag victims
* called.
* In this case, write_begin calls get_block and get original block
* number and previous defrag will be canceled.
*/
if (unlikely(__check_dfr_on(inode, (loff_t)(page->index << PAGE_SHIFT),
(loff_t)((page->index + 1) << PAGE_SHIFT), __func__))) {
struct buffer_head *head = page_buffers(page);
struct buffer_head *bh = head;
do {
clear_buffer_mapped(bh);
bh = bh->b_this_page;
} while (bh != head);
}
unlock_page(page);
if (boundary || (first_unmapped != blocks_per_page)) {
bio = mpage_bio_submit_write(0, bio);
if (boundary_block) {
write_boundary_block(boundary_bdev,
boundary_block, 1 << blkbits);
}
} else {
mpd->last_block_in_bio = blocks[blocks_per_page - 1];
}
goto out;
confused:
if (bio)
bio = mpage_bio_submit_write(0, bio);
if (mpd->use_writepage) {
ret = mapping->a_ops->writepage(page, wbc);
} else {
ret = -EAGAIN;
goto out;
}
/*
* The caller has a ref on the inode, so *mapping is stable
*/
mapping_set_error(mapping, ret);
out:
mpd->bio = bio;
return ret;
}
int sdfat_mpage_writepages(struct address_space *mapping,
struct writeback_control *wbc, get_block_t *get_block)
{
struct blk_plug plug;
int ret;
struct mpage_data mpd = {
.bio = NULL,
.last_block_in_bio = 0,
.get_block = get_block,
.use_writepage = 1,
.size_to_align = __calc_size_to_align(mapping->host->i_sb),
};
BUG_ON(!get_block);
blk_start_plug(&plug);
ret = write_cache_pages(mapping, wbc, sdfat_mpage_writepage, &mpd);
if (mpd.bio)
mpage_bio_submit_write(0, mpd.bio);
blk_finish_plug(&plug);
return ret;
}
#endif /* CONFIG_SDFAT_ALIGNED_MPAGE_WRITE */

478
fs/sdfat/nls.c Normal file
View File

@ -0,0 +1,478 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/************************************************************************/
/* */
/* PROJECT : exFAT & FAT12/16/32 File System */
/* FILE : nls.c */
/* PURPOSE : sdFAT NLS Manager */
/* */
/*----------------------------------------------------------------------*/
/* NOTES */
/* */
/* */
/************************************************************************/
#include <linux/string.h>
#include <linux/nls.h>
#include "sdfat.h"
#include "core.h"
/*----------------------------------------------------------------------*/
/* Global Variable Definitions */
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* Local Variable Definitions */
/*----------------------------------------------------------------------*/
static u16 bad_dos_chars[] = {
/* + , ; = [ ] */
0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D,
0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D,
0
};
/*
* Allow full-width illegal characters :
* "MS windows 7" supports full-width-invalid-name-characters.
* So we should check half-width-invalid-name-characters(ASCII) only
* for compatibility.
*
* " * / : < > ? \ |
*
* patch 1.2.0
*/
static u16 bad_uni_chars[] = {
0x0022, 0x002A, 0x002F, 0x003A,
0x003C, 0x003E, 0x003F, 0x005C, 0x007C,
#if 0 /* allow full-width characters */
0x201C, 0x201D, 0xFF0A, 0xFF0F, 0xFF1A,
0xFF1C, 0xFF1E, 0xFF1F, 0xFF3C, 0xFF5C,
#endif
0
};
/*----------------------------------------------------------------------*/
/* Local Function Declarations */
/*----------------------------------------------------------------------*/
static s32 convert_uni_to_ch(struct nls_table *nls, u16 uni, u8 *ch, s32 *lossy);
static s32 convert_ch_to_uni(struct nls_table *nls, u8 *ch, u16 *uni, s32 *lossy);
static u16 nls_upper(struct super_block *sb, u16 a)
{
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
if (SDFAT_SB(sb)->options.casesensitive)
return a;
if ((fsi->vol_utbl)[get_col_index(a)] != NULL)
return (fsi->vol_utbl)[get_col_index(a)][get_row_index(a)];
else
return a;
}
/*======================================================================*/
/* Global Function Definitions */
/*======================================================================*/
u16 *nls_wstrchr(u16 *str, u16 wchar)
{
while (*str) {
if (*(str++) == wchar)
return str;
}
return 0;
}
s32 nls_cmp_sfn(struct super_block *sb, u8 *a, u8 *b)
{
return strncmp((void *)a, (void *)b, DOS_NAME_LENGTH);
}
s32 nls_cmp_uniname(struct super_block *sb, u16 *a, u16 *b)
{
s32 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;
}
#define CASE_LOWER_BASE (0x08) /* base is lower case */
#define CASE_LOWER_EXT (0x10) /* extension is lower case */
s32 nls_uni16s_to_sfn(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname, s32 *p_lossy)
{
s32 i, j, len, lossy = NLS_NAME_NO_LOSSY;
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 = SDFAT_SB(sb)->nls_disk;
/* DOSNAME is filled with space */
for (i = 0; i < DOS_NAME_LENGTH; i++)
*(dosname+i) = ' ';
/* DOT and DOTDOT are handled by VFS layer */
/* search for the last embedded period */
last_period = NULL;
for (p = uniname; *p; p++) {
if (*p == (u16) '.')
last_period = p;
}
i = 0;
while (i < DOS_NAME_LENGTH) {
if (i == 8) {
if (last_period == NULL)
break;
if (uniname <= last_period) {
if (uniname < last_period)
lossy |= NLS_NAME_OVERLEN;
uniname = last_period + 1;
}
}
if (*uniname == (u16) '\0') {
break;
} else if (*uniname == (u16) ' ') {
lossy |= NLS_NAME_LOSSY;
} else if (*uniname == (u16) '.') {
if (uniname < last_period)
lossy |= NLS_NAME_LOSSY;
else
i = 8;
} else if (nls_wstrchr(bad_dos_chars, *uniname)) {
lossy |= NLS_NAME_LOSSY;
*(dosname+i) = '_';
i++;
} else {
len = convert_uni_to_ch(nls, *uniname, buf, &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');
lower |= (i < 8) ?
CASE_LOWER_BASE :
CASE_LOWER_EXT;
} else if ((*buf >= 'A') && (*buf <= 'Z')) {
*(dosname+i) = *buf;
upper |= (i < 8) ?
CASE_LOWER_BASE :
CASE_LOWER_EXT;
} else {
*(dosname+i) = *buf;
}
i++;
}
}
uniname++;
}
if (*dosname == 0xE5)
*dosname = 0x05;
if (*uniname != 0x0)
lossy |= NLS_NAME_OVERLEN;
if (upper & lower)
p_dosname->name_case = 0xFF;
else
p_dosname->name_case = lower;
if (p_lossy)
*p_lossy = lossy;
return i;
}
s32 nls_sfn_to_uni16s(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname)
{
s32 i = 0, j, n = 0;
u8 buf[MAX_DOSNAME_BUF_SIZE];
u8 *dosname = p_dosname->name;
u16 *uniname = p_uniname->name;
struct nls_table *nls = SDFAT_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 & CASE_LOWER_BASE))
*(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 & CASE_LOWER_EXT))
*(buf+n) = *(dosname+i) + ('a' - 'A');
else
*(buf+n) = *(dosname+i);
}
*(buf+n) = '\0';
i = j = 0;
while (j < MAX_NAME_LENGTH) {
if (*(buf+i) == '\0')
break;
i += convert_ch_to_uni(nls, (buf+i), uniname, NULL);
uniname++;
j++;
}
*uniname = (u16) '\0';
return j;
}
static s32 __nls_utf16s_to_vfsname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 buflen)
{
s32 len;
const u16 *uniname = p_uniname->name;
/* always len >= 0 */
len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH, UTF16_HOST_ENDIAN,
p_cstring, buflen);
p_cstring[len] = '\0';
return len;
}
static s32 __nls_vfsname_to_utf16s(struct super_block *sb, const u8 *p_cstring,
const s32 len, UNI_NAME_T *p_uniname, s32 *p_lossy)
{
s32 i, unilen, lossy = NLS_NAME_NO_LOSSY;
u16 upname[MAX_NAME_LENGTH+1];
u16 *uniname = p_uniname->name;
BUG_ON(!len);
unilen = utf8s_to_utf16s(p_cstring, len, UTF16_HOST_ENDIAN,
(wchar_t *)uniname, MAX_NAME_LENGTH+2);
if (unilen < 0) {
MMSG("%s: failed to vfsname_to_utf16(err:%d) "
"vfsnamelen:%d", __func__, unilen, len);
return unilen;
}
if (unilen > MAX_NAME_LENGTH) {
MMSG("%s: failed to vfsname_to_utf16(estr:ENAMETOOLONG) "
"vfsnamelen:%d, unilen:%d>%d",
__func__, len, unilen, MAX_NAME_LENGTH);
return -ENAMETOOLONG;
}
p_uniname->name_len = (u8)(unilen & 0xFF);
for (i = 0; i < unilen; i++) {
if ((*uniname < 0x0020) || nls_wstrchr(bad_uni_chars, *uniname))
lossy |= NLS_NAME_LOSSY;
*(upname+i) = nls_upper(sb, *uniname);
uniname++;
}
*uniname = (u16)'\0';
p_uniname->name_len = unilen;
p_uniname->name_hash = calc_chksum_2byte((void *) upname,
unilen << 1, 0, CS_DEFAULT);
if (p_lossy)
*p_lossy = lossy;
return unilen;
}
static s32 __nls_uni16s_to_vfsname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 buflen)
{
s32 i, j, len, out_len = 0;
u8 buf[MAX_CHARSET_SIZE];
const u16 *uniname = p_uniname->name;
struct nls_table *nls = SDFAT_SB(sb)->nls_io;
i = 0;
while ((i < MAX_NAME_LENGTH) && (out_len < (buflen-1))) {
if (*uniname == (u16)'\0')
break;
len = convert_uni_to_ch(nls, *uniname, buf, NULL);
if (out_len + len >= buflen)
len = (buflen - 1) - out_len;
out_len += len;
if (len > 1) {
for (j = 0; j < len; j++)
*p_cstring++ = (s8) *(buf+j);
} else { /* len == 1 */
*p_cstring++ = (s8) *buf;
}
uniname++;
i++;
}
*p_cstring = '\0';
return out_len;
}
static s32 __nls_vfsname_to_uni16s(struct super_block *sb, const u8 *p_cstring,
const s32 len, UNI_NAME_T *p_uniname, s32 *p_lossy)
{
s32 i, unilen, lossy = NLS_NAME_NO_LOSSY;
u16 upname[MAX_NAME_LENGTH+1];
u16 *uniname = p_uniname->name;
struct nls_table *nls = SDFAT_SB(sb)->nls_io;
BUG_ON(!len);
i = unilen = 0;
while ((unilen < MAX_NAME_LENGTH) && (i < len)) {
i += convert_ch_to_uni(nls, (u8 *)(p_cstring+i), uniname, &lossy);
if ((*uniname < 0x0020) || nls_wstrchr(bad_uni_chars, *uniname))
lossy |= NLS_NAME_LOSSY;
*(upname+unilen) = nls_upper(sb, *uniname);
uniname++;
unilen++;
}
if (*(p_cstring+i) != '\0')
lossy |= NLS_NAME_OVERLEN;
*uniname = (u16)'\0';
p_uniname->name_len = unilen;
p_uniname->name_hash =
calc_chksum_2byte((void *) upname, unilen<<1, 0, CS_DEFAULT);
if (p_lossy)
*p_lossy = lossy;
return unilen;
}
s32 nls_uni16s_to_vfsname(struct super_block *sb, UNI_NAME_T *uniname, u8 *p_cstring, s32 buflen)
{
if (SDFAT_SB(sb)->options.utf8)
return __nls_utf16s_to_vfsname(sb, uniname, p_cstring, buflen);
return __nls_uni16s_to_vfsname(sb, uniname, p_cstring, buflen);
}
s32 nls_vfsname_to_uni16s(struct super_block *sb, const u8 *p_cstring, const s32 len, UNI_NAME_T *uniname, s32 *p_lossy)
{
if (SDFAT_SB(sb)->options.utf8)
return __nls_vfsname_to_utf16s(sb, p_cstring, len, uniname, p_lossy);
return __nls_vfsname_to_uni16s(sb, p_cstring, len, uniname, p_lossy);
}
/*======================================================================*/
/* Local Function Definitions */
/*======================================================================*/
static s32 convert_ch_to_uni(struct nls_table *nls, u8 *ch, u16 *uni, s32 *lossy)
{
int len;
*uni = 0x0;
if (ch[0] < 0x80) {
*uni = (u16) ch[0];
return 1;
}
len = nls->char2uni(ch, MAX_CHARSET_SIZE, uni);
if (len < 0) {
/* conversion failed */
DMSG("%s: fail to use nls\n", __func__);
if (lossy != NULL)
*lossy |= NLS_NAME_LOSSY;
*uni = (u16) '_';
if (!strcmp(nls->charset, "utf8"))
return 1;
return 2;
}
return len;
} /* end of convert_ch_to_uni */
static s32 convert_uni_to_ch(struct nls_table *nls, u16 uni, u8 *ch, s32 *lossy)
{
int len;
ch[0] = 0x0;
if (uni < 0x0080) {
ch[0] = (u8) uni;
return 1;
}
len = nls->uni2char(uni, ch, MAX_CHARSET_SIZE);
if (len < 0) {
/* conversion failed */
DMSG("%s: fail to use nls\n", __func__);
if (lossy != NULL)
*lossy |= NLS_NAME_LOSSY;
ch[0] = '_';
return 1;
}
return len;
} /* end of convert_uni_to_ch */
/* end of nls.c */

5051
fs/sdfat/sdfat.c Normal file

File diff suppressed because it is too large Load Diff

507
fs/sdfat/sdfat.h Normal file
View File

@ -0,0 +1,507 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SDFAT_H
#define _SDFAT_H
#include <linux/buffer_head.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/nls.h>
#include <linux/fs.h>
#include <linux/mutex.h>
#include <linux/ratelimit.h>
#include <linux/version.h>
#include <linux/kobject.h>
#include "api.h"
#ifdef CONFIG_SDFAT_DFR
#include "dfr.h"
#endif
/*
* sdfat error flags
*/
#define SDFAT_ERRORS_CONT (1) /* ignore error and continue */
#define SDFAT_ERRORS_PANIC (2) /* panic on error */
#define SDFAT_ERRORS_RO (3) /* remount r/o on error */
/*
* sdfat allocator flags
*/
#define SDFAT_ALLOC_DELAY (1) /* Delayed allocation */
#define SDFAT_ALLOC_SMART (2) /* Smart allocation */
/*
* sdfat allocator destination for smart allocation
*/
#define ALLOC_NOWHERE (0)
#define ALLOC_COLD (1)
#define ALLOC_HOT (16)
#define ALLOC_COLD_ALIGNED (1)
#define ALLOC_COLD_PACKING (2)
#define ALLOC_COLD_SEQ (4)
/*
* sdfat nls lossy flag
*/
#define NLS_NAME_NO_LOSSY (0x00) /* no lossy */
#define NLS_NAME_LOSSY (0x01) /* just detected incorrect filename(s) */
#define NLS_NAME_OVERLEN (0x02) /* the length is over than its limit */
/*
* sdfat common MACRO
*/
#define CLUSTER_16(x) ((u16)((x) & 0xFFFFU))
#define CLUSTER_32(x) ((u32)((x) & 0xFFFFFFFFU))
#define CLUS_EOF CLUSTER_32(~0)
#define CLUS_BAD (0xFFFFFFF7U)
#define CLUS_FREE (0)
#define CLUS_BASE (2)
#define IS_CLUS_EOF(x) ((x) == CLUS_EOF)
#define IS_CLUS_BAD(x) ((x) == CLUS_BAD)
#define IS_CLUS_FREE(x) ((x) == CLUS_FREE)
#define IS_LAST_SECT_IN_CLUS(fsi, sec) \
((((sec) - (fsi)->data_start_sector + 1) \
& ((1 << (fsi)->sect_per_clus_bits) - 1)) == 0)
#define CLUS_TO_SECT(fsi, x) \
((((x) - CLUS_BASE) << (fsi)->sect_per_clus_bits) + (fsi)->data_start_sector)
#define SECT_TO_CLUS(fsi, sec) \
((((sec) - (fsi)->data_start_sector) >> (fsi)->sect_per_clus_bits) + CLUS_BASE)
/* variables defined at sdfat.c */
extern const char *FS_TYPE_STR[];
enum {
FS_TYPE_AUTO,
FS_TYPE_EXFAT,
FS_TYPE_VFAT,
FS_TYPE_MAX
};
/*
* sdfat mount in-memory data
*/
struct sdfat_mount_options {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
kuid_t fs_uid;
kgid_t fs_gid;
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) */
uid_t fs_uid;
gid_t fs_gid;
#endif
unsigned short fs_fmask;
unsigned short fs_dmask;
unsigned short allow_utime; /* permission for setting the [am]time */
unsigned short codepage; /* codepage for shortname conversions */
char *iocharset; /* charset for filename input/display */
struct {
unsigned int pack_ratio;
unsigned int sect_per_au;
unsigned int misaligned_sect;
} amap_opt; /* AMAP-related options (see amap.c) */
unsigned char utf8;
unsigned char casesensitive;
unsigned char adj_hidsect;
unsigned char tz_utc;
unsigned char improved_allocation;
unsigned char defrag;
unsigned char symlink; /* support symlink operation */
unsigned char errors; /* on error: continue, panic, remount-ro */
unsigned char discard; /* flag on if -o dicard specified and device support discard() */
unsigned char fs_type; /* fs_type that user specified */
unsigned short adj_req; /* support aligned mpage write */
};
#define SDFAT_HASH_BITS 8
#define SDFAT_HASH_SIZE (1UL << SDFAT_HASH_BITS)
/*
* SDFAT file system superblock in-memory data
*/
struct sdfat_sb_info {
FS_INFO_T fsi; /* private filesystem info */
struct mutex s_vlock; /* volume lock */
int use_vmalloc;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
int s_dirt;
struct mutex s_lock; /* superblock lock */
int write_super_queued; /* Write_super work is pending? */
struct delayed_work write_super_work; /* Work_queue data structrue for write_super() */
spinlock_t work_lock; /* Lock for WQ */
#endif
struct super_block *host_sb; /* sb pointer */
struct sdfat_mount_options options;
struct nls_table *nls_disk; /* Codepage used on disk */
struct nls_table *nls_io; /* Charset used for input and display */
struct ratelimit_state ratelimit;
spinlock_t inode_hash_lock;
struct hlist_head inode_hashtable[SDFAT_HASH_SIZE];
struct kobject sb_kobj;
#ifdef CONFIG_SDFAT_DBG_IOCTL
long debug_flags;
#endif /* CONFIG_SDFAT_DBG_IOCTL */
#ifdef CONFIG_SDFAT_DFR
struct defrag_info dfr_info;
struct completion dfr_complete;
unsigned int *dfr_new_clus;
int dfr_new_idx;
unsigned int *dfr_page_wb;
void **dfr_pagep;
unsigned int dfr_hint_clus;
unsigned int dfr_hint_idx;
int dfr_reserved_clus;
#ifdef CONFIG_SDFAT_DFR_DEBUG
int dfr_spo_flag;
#endif /* CONFIG_SDFAT_DFR_DEBUG */
#endif /* CONFIG_SDFAT_DFR */
#ifdef CONFIG_SDFAT_TRACE_IO
/* Statistics for allocator */
unsigned int stat_n_pages_written; /* # of written pages in total */
unsigned int stat_n_pages_added; /* # of added blocks in total */
unsigned int stat_n_bdev_pages_written; /* # of written pages owned by bdev inode */
unsigned int stat_n_pages_confused;
#endif
atomic_t stat_n_pages_queued; /* # of pages in the request queue (approx.) */
};
/*
* SDFAT file system inode in-memory data
*/
struct sdfat_inode_info {
FILE_ID_T fid;
char *target;
/* NOTE: i_size_ondisk is 64bits, so must hold ->inode_lock to access */
loff_t i_size_ondisk; /* physically allocated size */
loff_t i_size_aligned; /* block-aligned i_size (used in cont_write_begin) */
loff_t i_pos; /* on-disk position of directory entry or 0 */
struct hlist_node i_hash_fat; /* hash by i_location */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
struct rw_semaphore truncate_lock; /* protect bmap against truncate */
#endif
#ifdef CONFIG_SDFAT_DFR
struct defrag_info dfr_info;
#endif
struct inode vfs_inode;
};
/*
* FIXME : needs on-disk-slot in-memory data
*/
/* static inline functons */
static inline const char *sdfat_get_vol_type_str(unsigned int type)
{
if (type == EXFAT)
return "exfat";
else if (type == FAT32)
return "vfat:32";
else if (type == FAT16)
return "vfat:16";
else if (type == FAT12)
return "vfat:12";
return "unknown";
}
static inline struct sdfat_sb_info *SDFAT_SB(struct super_block *sb)
{
return (struct sdfat_sb_info *)sb->s_fs_info;
}
static inline struct sdfat_inode_info *SDFAT_I(struct inode *inode)
{
return container_of(inode, struct sdfat_inode_info, vfs_inode);
}
/*
* If ->i_mode can't hold S_IWUGO (i.e. ATTR_RO), we use ->i_attrs to
* save ATTR_RO instead of ->i_mode.
*
* If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only
* bit, it's just used as flag for app.
*/
static inline int sdfat_mode_can_hold_ro(struct inode *inode)
{
struct sdfat_sb_info *sbi = SDFAT_SB(inode->i_sb);
if (S_ISDIR(inode->i_mode))
return 0;
if ((~sbi->options.fs_fmask) & S_IWUGO)
return 1;
return 0;
}
/*
* FIXME : needs to check symlink option.
*/
/* Convert attribute bits and a mask to the UNIX mode. */
static inline mode_t sdfat_make_mode(struct sdfat_sb_info *sbi,
u32 attr, mode_t mode)
{
if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR))
mode &= ~S_IWUGO;
if (attr & ATTR_SUBDIR)
return (mode & ~sbi->options.fs_dmask) | S_IFDIR;
else if (attr & ATTR_SYMLINK)
return (mode & ~sbi->options.fs_dmask) | S_IFLNK;
else
return (mode & ~sbi->options.fs_fmask) | S_IFREG;
}
/* Return the FAT attribute byte for this inode */
static inline u32 sdfat_make_attr(struct inode *inode)
{
u32 attrs = SDFAT_I(inode)->fid.attr;
if (S_ISDIR(inode->i_mode))
attrs |= ATTR_SUBDIR;
if (sdfat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO))
attrs |= ATTR_READONLY;
return attrs;
}
static inline void sdfat_save_attr(struct inode *inode, u32 attr)
{
if (sdfat_mode_can_hold_ro(inode))
SDFAT_I(inode)->fid.attr = attr & ATTR_RWMASK;
else
SDFAT_I(inode)->fid.attr = attr & (ATTR_RWMASK | ATTR_READONLY);
}
/* sdfat/statistics.c */
/* bigdata function */
#ifdef CONFIG_SDFAT_STATISTICS
extern int sdfat_statistics_init(struct kset *sdfat_kset);
extern void sdfat_statistics_uninit(void);
extern void sdfat_statistics_set_mnt(FS_INFO_T *fsi);
extern void sdfat_statistics_set_mkdir(u8 flags);
extern void sdfat_statistics_set_create(u8 flags);
extern void sdfat_statistics_set_rw(u8 flags, u32 clu_offset, s32 create);
extern void sdfat_statistics_set_trunc(u8 flags, CHAIN_T *clu);
extern void sdfat_statistics_set_vol_size(struct super_block *sb);
#else
static inline int sdfat_statistics_init(struct kset *sdfat_kset)
{
return 0;
}
static inline void sdfat_statistics_uninit(void) {};
static inline void sdfat_statistics_set_mnt(FS_INFO_T *fsi) {};
static inline void sdfat_statistics_set_mkdir(u8 flags) {};
static inline void sdfat_statistics_set_create(u8 flags) {};
static inline void sdfat_statistics_set_rw(u8 flags, u32 clu_offset, s32 create) {};
static inline void sdfat_statistics_set_trunc(u8 flags, CHAIN_T *clu) {};
static inline void sdfat_statistics_set_vol_size(struct super_block *sb) {};
#endif
/* sdfat/nls.c */
/* NLS management function */
s32 nls_cmp_sfn(struct super_block *sb, u8 *a, u8 *b);
s32 nls_cmp_uniname(struct super_block *sb, u16 *a, u16 *b);
s32 nls_uni16s_to_sfn(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname, s32 *p_lossy);
s32 nls_sfn_to_uni16s(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname);
s32 nls_uni16s_to_vfsname(struct super_block *sb, UNI_NAME_T *uniname, u8 *p_cstring, s32 len);
s32 nls_vfsname_to_uni16s(struct super_block *sb, const u8 *p_cstring,
const s32 len, UNI_NAME_T *uniname, s32 *p_lossy);
/* sdfat/mpage.c */
#ifdef CONFIG_SDFAT_ALIGNED_MPAGE_WRITE
int sdfat_mpage_writepages(struct address_space *mapping,
struct writeback_control *wbc, get_block_t *get_block);
#endif
/* sdfat/xattr.c */
#ifdef CONFIG_SDFAT_VIRTUAL_XATTR
void setup_sdfat_xattr_handler(struct super_block *sb);
extern int sdfat_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags);
extern ssize_t sdfat_getxattr(struct dentry *dentry, const char *name, void *value, size_t size);
extern ssize_t sdfat_listxattr(struct dentry *dentry, char *list, size_t size);
extern int sdfat_removexattr(struct dentry *dentry, const char *name);
#else
static inline void setup_sdfat_xattr_handler(struct super_block *sb) {};
#endif
/* sdfat/misc.c */
extern void
__sdfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
__printf(3, 4) __cold;
#define sdfat_fs_error(sb, fmt, args...) \
__sdfat_fs_error(sb, 1, fmt, ## args)
#define sdfat_fs_error_ratelimit(sb, fmt, args...) \
__sdfat_fs_error(sb, __ratelimit(&SDFAT_SB(sb)->ratelimit), fmt, ## args)
extern void
__sdfat_msg(struct super_block *sb, const char *lv, int st, const char *fmt, ...)
__printf(4, 5) __cold;
#define sdfat_msg(sb, lv, fmt, args...) \
__sdfat_msg(sb, lv, 0, fmt, ## args)
#define sdfat_log_msg(sb, lv, fmt, args...) \
__sdfat_msg(sb, lv, 1, fmt, ## args)
extern void sdfat_log_version(void);
extern void sdfat_time_fat2unix(struct sdfat_sb_info *sbi, struct timespec *ts,
DATE_TIME_T *tp);
extern void sdfat_time_unix2fat(struct sdfat_sb_info *sbi, struct timespec *ts,
DATE_TIME_T *tp);
extern TIMESTAMP_T *tm_now(struct sdfat_sb_info *sbi, TIMESTAMP_T *tm);
#ifdef CONFIG_SDFAT_DEBUG
#ifdef CONFIG_SDFAT_DBG_CAREFUL
void sdfat_debug_check_clusters(struct inode *inode);
#else
#define sdfat_debug_check_clusters(inode)
#endif /* CONFIG_SDFAT_DBG_CAREFUL */
#ifdef CONFIG_SDFAT_DBG_BUGON
#define sdfat_debug_bug_on(expr) BUG_ON(expr)
#else
#define sdfat_debug_bug_on(expr)
#endif
#ifdef CONFIG_SDFAT_DBG_WARNON
#define sdfat_debug_warn_on(expr) WARN_ON(expr)
#else
#define sdfat_debug_warn_on(expr)
#endif
#else /* CONFIG_SDFAT_DEBUG */
#define sdfat_debug_check_clusters(inode)
#define sdfat_debug_bug_on(expr)
#endif /* CONFIG_SDFAT_DEBUG */
#ifdef CONFIG_SDFAT_TRACE_ELAPSED_TIME
u32 sdfat_time_current_usec(struct timeval *tv);
extern struct timeval __t1;
extern struct timeval __t2;
#define TIME_GET(tv) sdfat_time_current_usec(tv)
#define TIME_START(s) sdfat_time_current_usec(s)
#define TIME_END(e) sdfat_time_current_usec(e)
#define TIME_ELAPSED(s, e) ((u32)(((e)->tv_sec - (s)->tv_sec) * 1000000 + \
((e)->tv_usec - (s)->tv_usec)))
#define PRINT_TIME(n) pr_info("[SDFAT] Elapsed time %d = %d (usec)\n", n, (__t2 - __t1))
#else /* CONFIG_SDFAT_TRACE_ELAPSED_TIME */
#define TIME_GET(tv) (0)
#define TIME_START(s)
#define TIME_END(e)
#define TIME_ELAPSED(s, e) (0)
#define PRINT_TIME(n)
#endif /* CONFIG_SDFAT_TRACE_ELAPSED_TIME */
#define SDFAT_MSG_LV_NONE (0x00000000)
#define SDFAT_MSG_LV_ERR (0x00000001)
#define SDFAT_MSG_LV_INFO (0x00000002)
#define SDFAT_MSG_LV_DBG (0x00000003)
#define SDFAT_MSG_LV_MORE (0x00000004)
#define SDFAT_MSG_LV_TRACE (0x00000005)
#define SDFAT_MSG_LV_ALL (0x00000006)
#define SDFAT_MSG_LEVEL SDFAT_MSG_LV_INFO
#define SDFAT_TAG_NAME "SDFAT"
#define __S(x) #x
#define _S(x) __S(x)
extern void __sdfat_dmsg(int level, const char *fmt, ...) __printf(2, 3) __cold;
#define SDFAT_EMSG_T(level, ...) \
__sdfat_dmsg(level, KERN_ERR "[" SDFAT_TAG_NAME "] [" _S(__FILE__) "(" _S(__LINE__) ")] " __VA_ARGS__)
#define SDFAT_DMSG_T(level, ...) \
__sdfat_dmsg(level, KERN_INFO "[" SDFAT_TAG_NAME "] " __VA_ARGS__)
#define SDFAT_EMSG(...) SDFAT_EMSG_T(SDFAT_MSG_LV_ERR, __VA_ARGS__)
#define SDFAT_IMSG(...) SDFAT_DMSG_T(SDFAT_MSG_LV_INFO, __VA_ARGS__)
#define SDFAT_DMSG(...) SDFAT_DMSG_T(SDFAT_MSG_LV_DBG, __VA_ARGS__)
#define SDFAT_MMSG(...) SDFAT_DMSG_T(SDFAT_MSG_LV_MORE, __VA_ARGS__)
#define SDFAT_TMSG(...) SDFAT_DMSG_T(SDFAT_MSG_LV_TRACE, __VA_ARGS__)
#define EMSG(...)
#define IMSG(...)
#define DMSG(...)
#define MMSG(...)
#define TMSG(...)
#define EMSG_VAR(exp)
#define IMSG_VAR(exp)
#define DMSG_VAR(exp)
#define MMSG_VAR(exp)
#define TMSG_VAR(exp)
#ifdef CONFIG_SDFAT_DBG_MSG
#if (SDFAT_MSG_LEVEL >= SDFAT_MSG_LV_ERR)
#undef EMSG
#undef EMSG_VAR
#define EMSG(...) SDFAT_EMSG(__VA_ARGS__)
#define EMSG_VAR(exp) exp
#endif
#if (SDFAT_MSG_LEVEL >= SDFAT_MSG_LV_INFO)
#undef IMSG
#undef IMSG_VAR
#define IMSG(...) SDFAT_IMSG(__VA_ARGS__)
#define IMSG_VAR(exp) exp
#endif
#if (SDFAT_MSG_LEVEL >= SDFAT_MSG_LV_DBG)
#undef DMSG
#undef DMSG_VAR
#define DMSG(...) SDFAT_DMSG(__VA_ARGS__)
#define DMSG_VAR(exp) exp
#endif
#if (SDFAT_MSG_LEVEL >= SDFAT_MSG_LV_MORE)
#undef MMSG
#undef MMSG_VAR
#define MMSG(...) SDFAT_MMSG(__VA_ARGS__)
#define MMSG_VAR(exp) exp
#endif
/* should replace with trace function */
#if (SDFAT_MSG_LEVEL >= SDFAT_MSG_LV_TRACE)
#undef TMSG
#undef TMSG_VAR
#define TMSG(...) SDFAT_TMSG(__VA_ARGS__)
#define TMSG_VAR(exp) exp
#endif
#endif /* CONFIG_SDFAT_DBG_MSG */
#define ASSERT(expr) { \
if (!(expr)) { \
pr_err("Assertion failed! %s\n", #expr); \
BUG_ON(1); \
} \
}
#endif /* !_SDFAT_H */

416
fs/sdfat/sdfat_fs.h Normal file
View File

@ -0,0 +1,416 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SDFAT_FS_H
#define _SDFAT_FS_H
#include <linux/types.h>
#include <linux/magic.h>
#include <asm/byteorder.h>
/*----------------------------------------------------------------------*/
/* Constant & Macro Definitions */
/*----------------------------------------------------------------------*/
#ifndef MSDOS_SUPER_MAGIC
#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */
#endif
#ifndef EXFAT_SUPER_MAGIC
#define EXFAT_SUPER_MAGIC (0x2011BAB0UL)
#endif /* EXFAT_SUPER_MAGIC */
#define SDFAT_SUPER_MAGIC (0x5EC5DFA4UL)
#define SDFAT_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
/* directory file name */
#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
/* file name lengths */
/* NOTE :
* The maximum length of input or output is limited to 256 including NULL,
* But we allocate 4 extra bytes for utf8 translation reside in last position,
* because utf8 can uses memory upto 6 bytes per one character.
* Therefore, MAX_CHARSET_SIZE supports upto 6 bytes for utf8
*/
#define MAX_UNINAME_BUF_SIZE (((MAX_NAME_LENGTH+1)*2)+4)
#define MAX_DOSNAME_BUF_SIZE ((DOS_NAME_LENGTH+2)+6)
#define MAX_VFSNAME_BUF_SIZE ((MAX_NAME_LENGTH+1)*MAX_CHARSET_SIZE)
#define MAX_CHARSET_SIZE 6 // max size of multi-byte character
#define MAX_NAME_LENGTH 255 // max len of file name excluding NULL
#define DOS_NAME_LENGTH 11 // DOS file name length excluding NULL
#define DENTRY_SIZE 32 /* directory entry size */
#define DENTRY_SIZE_BITS 5
#define MAX_FAT_DENTRIES 65536 /* FAT allows 65536 directory entries */
#define MAX_EXFAT_DENTRIES 8388608 /* exFAT allows 8388608(256MB) directory entries */
/* 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
#define FAT_VOL_DIRTY 0x01
/* 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
/* dentry types */
#define MSDOS_DELETED 0xE5 /* deleted mark */
#define MSDOS_UNUSED 0x00 /* end of directory */
#define EXFAT_UNUSED 0x00 /* end of directory */
#define IS_EXFAT_DELETED(x) ((x) < 0x80) /* deleted file (0x01~0x7F) */
#define EXFAT_INVAL 0x80 /* invalid value */
#define EXFAT_BITMAP 0x81 /* allocation bitmap */
#define EXFAT_UPCASE 0x82 /* upcase table */
#define EXFAT_VOLUME 0x83 /* volume label */
#define EXFAT_FILE 0x85 /* file or dir */
#define EXFAT_STREAM 0xC0 /* stream entry */
#define EXFAT_NAME 0xC1 /* file name entry */
#define EXFAT_ACL 0xC2 /* stream entry */
/* specific flag */
#define MSDOS_LAST_LFN 0x40
/* 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 (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | \
ATTR_VOLUME) /* 0x000F */
#define ATTR_EXTEND_MASK (ATTR_EXTEND | ATTR_SUBDIR | ATTR_ARCHIVE)
#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \
ATTR_SUBDIR | ATTR_ARCHIVE | ATTR_SYMLINK)/* 0x007E */
/* file creation modes */
#define FM_REGULAR 0x00
#define FM_SYMLINK 0x40
/* 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
/*
* ioctl command
*/
#define SDFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32)
#define SDFAT_IOCTL_DFR_INFO _IOC(_IOC_NONE, 'E', 0x13, sizeof(u32))
#define SDFAT_IOCTL_DFR_TRAV _IOC(_IOC_NONE, 'E', 0x14, sizeof(u32))
#define SDFAT_IOCTL_DFR_REQ _IOC(_IOC_NONE, 'E', 0x15, sizeof(u32))
#define SDFAT_IOCTL_DFR_SPO_FLAG _IOC(_IOC_NONE, 'E', 0x16, sizeof(u32))
#define SDFAT_IOCTL_PANIC _IOC(_IOC_NONE, 'E', 0x17, sizeof(u32))
/*
* ioctl command for debugging
*/
/*
* 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
*
* NOTE : This is available only If CONFIG_SDFAT_DVBG_IOCTL is enabled.
*
*/
#define SDFAT_IOC_GET_DEBUGFLAGS _IOR('f', 100, long)
#define SDFAT_IOC_SET_DEBUGFLAGS _IOW('f', 101, long)
#define SDFAT_DEBUGFLAGS_INVALID_UMOUNT 0x01
#define SDFAT_DEBUGFLAGS_ERROR_RW 0x02
/*----------------------------------------------------------------------*/
/* On-Disk Type Definitions */
/*----------------------------------------------------------------------*/
/* FAT12/16 BIOS parameter block (64 bytes) */
typedef struct {
__u8 jmp_boot[3];
__u8 oem_name[8];
__u8 sect_size[2]; /* unaligned */
__u8 sect_per_clus;
__le16 num_reserved; /* . */
__u8 num_fats;
__u8 num_root_entries[2]; /* unaligned */
__u8 num_sectors[2]; /* unaligned */
__u8 media_type;
__le16 num_fat_sectors;
__le16 sectors_in_track;
__le16 num_heads;
__le32 num_hid_sectors; /* . */
__le32 num_huge_sectors;
__u8 phy_drv_no;
__u8 state; /* used by WindowsNT for mount state */
__u8 ext_signature;
__u8 vol_serial[4];
__u8 vol_label[11];
__u8 vol_type[8];
__le16 dummy;
} bpb16_t;
/* FAT32 BIOS parameter block (64 bytes) */
typedef struct {
__u8 jmp_boot[3];
__u8 oem_name[8];
__u8 sect_size[2]; /* unaligned */
__u8 sect_per_clus;
__le16 num_reserved;
__u8 num_fats;
__u8 num_root_entries[2]; /* unaligned */
__u8 num_sectors[2]; /* unaligned */
__u8 media_type;
__le16 num_fat_sectors; /* zero */
__le16 sectors_in_track;
__le16 num_heads;
__le32 num_hid_sectors; /* . */
__le32 num_huge_sectors;
__le32 num_fat32_sectors;
__le16 ext_flags;
__u8 fs_version[2];
__le32 root_cluster; /* . */
__le16 fsinfo_sector;
__le16 backup_sector;
__le16 reserved[6]; /* . */
} bpb32_t;
/* FAT32 EXTEND BIOS parameter block (32 bytes) */
typedef struct {
__u8 phy_drv_no;
__u8 state; /* used by WindowsNT for mount state */
__u8 ext_signature;
__u8 vol_serial[4];
__u8 vol_label[11];
__u8 vol_type[8];
__le16 dummy[3];
} bsx32_t;
/* EXFAT BIOS parameter block (64 bytes) */
typedef struct {
__u8 jmp_boot[3];
__u8 oem_name[8];
__u8 res_zero[53];
} bpb64_t;
/* EXFAT EXTEND BIOS parameter block (56 bytes) */
typedef struct {
__le64 vol_offset;
__le64 vol_length;
__le32 fat_offset;
__le32 fat_length;
__le32 clu_offset;
__le32 clu_count;
__le32 root_cluster;
__le32 vol_serial;
__u8 fs_version[2];
__le16 vol_flags;
__u8 sect_size_bits;
__u8 sect_per_clus_bits;
__u8 num_fats;
__u8 phy_drv_no;
__u8 perc_in_use;
__u8 reserved2[7];
} bsx64_t;
/* FAT32 PBR (64 bytes) */
typedef struct {
bpb16_t bpb;
} pbr16_t;
/* FAT32 PBR[BPB+BSX] (96 bytes) */
typedef struct {
bpb32_t bpb;
bsx32_t bsx;
} pbr32_t;
/* EXFAT PBR[BPB+BSX] (120 bytes) */
typedef struct {
bpb64_t bpb;
bsx64_t bsx;
} pbr64_t;
/* Common PBR[Partition Boot Record] (512 bytes) */
typedef struct {
union {
__u8 raw[64];
bpb16_t f16;
bpb32_t f32;
bpb64_t f64;
} bpb;
union {
__u8 raw[56];
bsx32_t f32;
bsx64_t f64;
} bsx;
__u8 boot_code[390];
__le16 signature;
} pbr_t;
/* FAT32 filesystem information sector (512 bytes) */
typedef struct {
__le32 signature1; // aligned
__u8 reserved1[480];
__le32 signature2; // aligned
__le32 free_cluster; // aligned
__le32 next_cluster; // aligned
__u8 reserved2[14];
__le16 signature3[2];
} fat32_fsi_t;
/* FAT directory entry (32 bytes) */
typedef struct {
__u8 dummy[32];
} DENTRY_T;
typedef struct {
__u8 name[DOS_NAME_LENGTH]; /* 11 chars */
__u8 attr;
__u8 lcase;
__u8 create_time_ms;
__le16 create_time; // aligned
__le16 create_date; // aligned
__le16 access_date; // aligned
__le16 start_clu_hi; // aligned
__le16 modify_time; // aligned
__le16 modify_date; // aligned
__le16 start_clu_lo; // aligned
__le32 size; // aligned
} DOS_DENTRY_T;
/* FAT extended directory entry (32 bytes) */
typedef struct {
__u8 order;
__u8 unicode_0_4[10];
__u8 attr;
__u8 sysid;
__u8 checksum;
__le16 unicode_5_10[6]; // aligned
__le16 start_clu; // aligned
__le16 unicode_11_12[2]; // aligned
} EXT_DENTRY_T;
/* EXFAT file directory entry (32 bytes) */
typedef struct {
__u8 type;
__u8 num_ext;
__le16 checksum; // aligned
__le16 attr; // aligned
__le16 reserved1;
__le16 create_time; // aligned
__le16 create_date; // aligned
__le16 modify_time; // aligned
__le16 modify_date; // aligned
__le16 access_time; // aligned
__le16 access_date; // aligned
__u8 create_time_ms;
__u8 modify_time_ms;
__u8 access_time_ms;
__u8 reserved2[9];
} FILE_DENTRY_T;
/* EXFAT stream extension directory entry (32 bytes) */
typedef struct {
__u8 type;
__u8 flags;
__u8 reserved1;
__u8 name_len;
__le16 name_hash; // aligned
__le16 reserved2;
__le64 valid_size; // aligned
__le32 reserved3; // aligned
__le32 start_clu; // aligned
__le64 size; // aligned
} STRM_DENTRY_T;
/* EXFAT file name directory entry (32 bytes) */
typedef struct {
__u8 type;
__u8 flags;
__le16 unicode_0_14[15]; // aligned
} NAME_DENTRY_T;
/* EXFAT allocation bitmap directory entry (32 bytes) */
typedef struct {
__u8 type;
__u8 flags;
__u8 reserved[18];
__le32 start_clu; // aligned
__le64 size; // aligned
} BMAP_DENTRY_T;
/* EXFAT up-case table directory entry (32 bytes) */
typedef struct {
__u8 type;
__u8 reserved1[3];
__le32 checksum; // aligned
__u8 reserved2[12];
__le32 start_clu; // aligned
__le64 size; // aligned
} CASE_DENTRY_T;
/* EXFAT volume label directory entry (32 bytes) */
typedef struct {
__u8 type;
__u8 label_len;
__le16 unicode_0_10[11]; // aligned
__u8 reserved[8];
} VOLM_DENTRY_T;
#endif /* _SDFAT_FS_H */

262
fs/sdfat/statistics.c Normal file
View File

@ -0,0 +1,262 @@
#include "sdfat.h"
#define SDFAT_VF_CLUS_MAX 7 /* 512 Byte ~ 32 KByte */
#define SDFAT_EF_CLUS_MAX 17 /* 512 Byte ~ 32 MByte */
enum {
SDFAT_MNT_FAT12,
SDFAT_MNT_FAT16,
SDFAT_MNT_FAT32,
SDFAT_MNT_EXFAT,
SDFAT_MNT_MAX
};
enum {
SDFAT_OP_EXFAT_MNT,
SDFAT_OP_MKDIR,
SDFAT_OP_CREATE,
SDFAT_OP_READ,
SDFAT_OP_WRITE,
SDFAT_OP_TRUNC,
SDFAT_OP_MAX
};
enum {
SDFAT_VOL_4G,
SDFAT_VOL_8G,
SDFAT_VOL_16G,
SDFAT_VOL_32G,
SDFAT_VOL_64G,
SDFAT_VOL_128G,
SDFAT_VOL_256G,
SDFAT_VOL_512G,
SDFAT_VOL_XTB,
SDFAT_VOL_MAX
};
static struct sdfat_statistics {
u32 clus_vfat[SDFAT_VF_CLUS_MAX];
u32 clus_exfat[SDFAT_EF_CLUS_MAX];
u32 mnt_cnt[SDFAT_MNT_MAX];
u32 nofat_op[SDFAT_OP_MAX];
u32 vol_size[SDFAT_VOL_MAX];
} statistics;
static struct kset *sdfat_statistics_kset;
static ssize_t vfat_cl_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buff)
{
return snprintf(buff, PAGE_SIZE, "VCL_512B_I:%u,VCL_1K_I:%u,VCL_2K_I:%u,"
"VCL_4K_I:%u,VCL_8K_I:%u,VCL_16K_I:%u,VCL_32K_I:%u\n",
statistics.clus_vfat[0], statistics.clus_vfat[1],
statistics.clus_vfat[2], statistics.clus_vfat[3],
statistics.clus_vfat[4], statistics.clus_vfat[5],
statistics.clus_vfat[6]);
}
static ssize_t exfat_cl_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buff)
{
return snprintf(buff, PAGE_SIZE, "ECL_512B_I:%u,ECL_1K_I:%u,ECL_2K_I:%u,"
"ECL_4K_I:%u,ECL_8K_I:%u,ECL_16K_I:%u,ECL_32K_I:%u,ECL_64K_I:%u,"
"ECL_128K_I:%u,ECL_256K_I:%u,ECL_512K_I:%u,ECL_1M_I:%u,"
"ECL_2M_I:%u,ECL_4M_I:%u,ECL_8M_I:%u,ECL_16M_I:%u,ECL_32M_I:%u\n",
statistics.clus_exfat[0], statistics.clus_exfat[1],
statistics.clus_exfat[2], statistics.clus_exfat[3],
statistics.clus_exfat[4], statistics.clus_exfat[5],
statistics.clus_exfat[6], statistics.clus_exfat[7],
statistics.clus_exfat[8], statistics.clus_exfat[9],
statistics.clus_exfat[10], statistics.clus_exfat[11],
statistics.clus_exfat[12], statistics.clus_exfat[13],
statistics.clus_exfat[14], statistics.clus_exfat[15],
statistics.clus_exfat[16]);
}
static ssize_t mount_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buff)
{
return snprintf(buff, PAGE_SIZE, "FAT12_MNT_I:%u,FAT16_MNT_I:%u,FAT32_MNT_I:%u,"
"EXFAT_MNT_I:%u\n",
statistics.mnt_cnt[SDFAT_MNT_FAT12],
statistics.mnt_cnt[SDFAT_MNT_FAT16],
statistics.mnt_cnt[SDFAT_MNT_FAT32],
statistics.mnt_cnt[SDFAT_MNT_EXFAT]);
}
static ssize_t nofat_op_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buff)
{
return snprintf(buff, PAGE_SIZE, "NOFAT_MOUNT_I:%u,NOFAT_MKDIR_I:%u,NOFAT_CREATE_I:%u,"
"NOFAT_READ_I:%u,NOFAT_WRITE_I:%u,NOFAT_TRUNC_I:%u\n",
statistics.nofat_op[SDFAT_OP_EXFAT_MNT],
statistics.nofat_op[SDFAT_OP_MKDIR],
statistics.nofat_op[SDFAT_OP_CREATE],
statistics.nofat_op[SDFAT_OP_READ],
statistics.nofat_op[SDFAT_OP_WRITE],
statistics.nofat_op[SDFAT_OP_TRUNC]);
}
static ssize_t vol_size_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buff)
{
return snprintf(buff, PAGE_SIZE, "VOL_4G_I:%u,VOL_8G_I:%u,VOL_16G_I:%u,"
"VOL_32G_I:%u,VOL_64G_I:%u,VOL_128G_I:%u,VOL_256G_I:%u,"
"VOL_512G_I:%u,VOL_XTB_I:%u\n",
statistics.vol_size[SDFAT_VOL_4G],
statistics.vol_size[SDFAT_VOL_8G],
statistics.vol_size[SDFAT_VOL_16G],
statistics.vol_size[SDFAT_VOL_32G],
statistics.vol_size[SDFAT_VOL_64G],
statistics.vol_size[SDFAT_VOL_128G],
statistics.vol_size[SDFAT_VOL_256G],
statistics.vol_size[SDFAT_VOL_512G],
statistics.vol_size[SDFAT_VOL_XTB]);
}
static struct kobj_attribute vfat_cl_attr = __ATTR_RO(vfat_cl);
static struct kobj_attribute exfat_cl_attr = __ATTR_RO(exfat_cl);
static struct kobj_attribute mount_attr = __ATTR_RO(mount);
static struct kobj_attribute nofat_op_attr = __ATTR_RO(nofat_op);
static struct kobj_attribute vol_size_attr = __ATTR_RO(vol_size);
static struct attribute *attributes_statistics[] = {
&vfat_cl_attr.attr,
&exfat_cl_attr.attr,
&mount_attr.attr,
&nofat_op_attr.attr,
&vol_size_attr.attr,
NULL,
};
static struct attribute_group attr_group_statistics = {
.attrs = attributes_statistics,
};
int sdfat_statistics_init(struct kset *sdfat_kset)
{
int err;
sdfat_statistics_kset = kset_create_and_add("statistics", NULL, &sdfat_kset->kobj);
if (!sdfat_statistics_kset) {
pr_err("[SDFAT] failed to create sdfat statistics kobj\n");
return -ENOMEM;
}
err = sysfs_create_group(&sdfat_statistics_kset->kobj, &attr_group_statistics);
if (err) {
pr_err("[SDFAT] failed to create sdfat statistics attributes\n");
kset_unregister(sdfat_statistics_kset);
sdfat_statistics_kset = NULL;
return err;
}
return 0;
}
void sdfat_statistics_uninit(void)
{
if (sdfat_statistics_kset) {
sysfs_remove_group(&sdfat_statistics_kset->kobj, &attr_group_statistics);
kset_unregister(sdfat_statistics_kset);
sdfat_statistics_kset = NULL;
}
memset(&statistics, 0, sizeof(struct sdfat_statistics));
}
void sdfat_statistics_set_mnt(FS_INFO_T *fsi)
{
if (fsi->vol_type == EXFAT) {
statistics.mnt_cnt[SDFAT_MNT_EXFAT]++;
statistics.nofat_op[SDFAT_OP_EXFAT_MNT] = 1;
if (fsi->sect_per_clus_bits < SDFAT_EF_CLUS_MAX)
statistics.clus_exfat[fsi->sect_per_clus_bits]++;
else
statistics.clus_exfat[SDFAT_EF_CLUS_MAX - 1]++;
return;
}
if (fsi->vol_type == FAT32)
statistics.mnt_cnt[SDFAT_MNT_FAT32]++;
else if (fsi->vol_type == FAT16)
statistics.mnt_cnt[SDFAT_MNT_FAT16]++;
else if (fsi->vol_type == FAT12)
statistics.mnt_cnt[SDFAT_MNT_FAT12]++;
if (fsi->sect_per_clus_bits < SDFAT_VF_CLUS_MAX)
statistics.clus_vfat[fsi->sect_per_clus_bits]++;
else
statistics.clus_vfat[SDFAT_VF_CLUS_MAX - 1]++;
}
void sdfat_statistics_set_mkdir(u8 flags)
{
if (flags != 0x03)
return;
statistics.nofat_op[SDFAT_OP_MKDIR] = 1;
}
void sdfat_statistics_set_create(u8 flags)
{
if (flags != 0x03)
return;
statistics.nofat_op[SDFAT_OP_CREATE] = 1;
}
/* flags : file or dir flgas, 0x03 means no fat-chain.
* clu_offset : file or dir logical cluster offset
* create : BMAP_ADD_CLUSTER or not
*
* File or dir have BMAP_ADD_CLUSTER is no fat-chain write
* when they have 0x03 flag and two or more clusters.
* And don`t have BMAP_ADD_CLUSTER is no fat-chain read
* when above same condition.
*/
void sdfat_statistics_set_rw(u8 flags, u32 clu_offset, s32 create)
{
if ((flags == 0x03) && (clu_offset > 1)) {
if (create)
statistics.nofat_op[SDFAT_OP_WRITE] = 1;
else
statistics.nofat_op[SDFAT_OP_READ] = 1;
}
}
/* flags : file or dir flgas, 0x03 means no fat-chain.
* clu : cluster chain
*
* Set no fat-chain trunc when file or dir have 0x03 flag
* and tow or more clusters.
*/
void sdfat_statistics_set_trunc(u8 flags, CHAIN_T *clu)
{
if ((flags == 0x03) && (clu->size > 1))
statistics.nofat_op[SDFAT_OP_TRUNC] = 1;
}
void sdfat_statistics_set_vol_size(struct super_block *sb)
{
u64 vol_size;
FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
vol_size = (u64)fsi->num_sectors << sb->s_blocksize_bits;
if (vol_size <= ((u64)1 << 32))
statistics.vol_size[SDFAT_VOL_4G]++;
else if (vol_size <= ((u64)1 << 33))
statistics.vol_size[SDFAT_VOL_8G]++;
else if (vol_size <= ((u64)1 << 34))
statistics.vol_size[SDFAT_VOL_16G]++;
else if (vol_size <= ((u64)1 << 35))
statistics.vol_size[SDFAT_VOL_32G]++;
else if (vol_size <= ((u64)1 << 36))
statistics.vol_size[SDFAT_VOL_64G]++;
else if (vol_size <= ((u64)1 << 37))
statistics.vol_size[SDFAT_VOL_128G]++;
else if (vol_size <= ((u64)1 << 38))
statistics.vol_size[SDFAT_VOL_256G]++;
else if (vol_size <= ((u64)1 << 39))
statistics.vol_size[SDFAT_VOL_512G]++;
else
statistics.vol_size[SDFAT_VOL_XTB]++;
}

407
fs/sdfat/upcase.h Normal file
View File

@ -0,0 +1,407 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _UPCASE_H
#define _UPCASE_H
/* Upcase tabel macro */
#define SDFAT_NUM_UPCASE 2918
#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;
}
static const u8 uni_def_upcase[SDFAT_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
};
#endif /* _UPCASE_H */

25
fs/sdfat/version.h Normal file
View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/************************************************************************/
/* */
/* PROJECT : exFAT & FAT12/16/32 File System */
/* FILE : version.h */
/* PURPOSE : sdFAT File Manager */
/* */
/************************************************************************/
#define SDFAT_VERSION "1.4.16"

132
fs/sdfat/xattr.c Normal file
View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/************************************************************************/
/* */
/* PROJECT : exFAT & FAT12/16/32 File System */
/* FILE : xattr.c */
/* PURPOSE : sdFAT code for supporting xattr(Extended File Attributes) */
/* */
/*----------------------------------------------------------------------*/
/* NOTES */
/* */
/* */
/************************************************************************/
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/xattr.h>
#include <linux/dcache.h>
#include "sdfat.h"
#ifndef CONFIG_SDFAT_VIRTUAL_XATTR_SELINUX_LABEL
#define CONFIG_SDFAT_VIRTUAL_XATTR_SELINUX_LABEL ("undefined")
#endif
static const char default_xattr[] = CONFIG_SDFAT_VIRTUAL_XATTR_SELINUX_LABEL;
static int can_support(const char *name)
{
if (!name || strcmp(name, "security.selinux"))
return -1;
return 0;
}
ssize_t sdfat_listxattr(struct dentry *dentry, char *list, size_t size)
{
return 0;
}
/*************************************************************************
* INNER FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY
*************************************************************************/
static int __sdfat_xattr_check_support(const char *name)
{
if (can_support(name))
return -EOPNOTSUPP;
return 0;
}
ssize_t __sdfat_getxattr(const char *name, void *value, size_t size)
{
if (can_support(name))
return -EOPNOTSUPP;
if ((size > strlen(default_xattr)+1) && value)
strcpy(value, default_xattr);
return strlen(default_xattr);
}
/*************************************************************************
* FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY
*************************************************************************/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
static int sdfat_xattr_get(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, void *buffer, size_t size)
{
return __sdfat_getxattr(name, buffer, size);
}
static int sdfat_xattr_set(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, const void *value, size_t size,
int flags)
{
return __sdfat_xattr_check_support(name);
}
const struct xattr_handler sdfat_xattr_handler = {
.prefix = "", /* match anything */
.get = sdfat_xattr_get,
.set = sdfat_xattr_set,
};
const struct xattr_handler *sdfat_xattr_handlers[] = {
&sdfat_xattr_handler,
NULL
};
void setup_sdfat_xattr_handler(struct super_block *sb)
{
sb->s_xattr = sdfat_xattr_handlers;
}
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) */
int sdfat_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags)
{
return __sdfat_xattr_check_support(name);
}
ssize_t sdfat_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
{
return __sdfat_getxattr(name, value, size);
}
int sdfat_removexattr(struct dentry *dentry, const char *name)
{
return __sdfat_xattr_check_support(name);
}
void setup_sdfat_xattr_handler(struct super_block *sb)
{
/* DO NOTHING */
}
#endif