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:
parent
4aedbbf079
commit
72c5a7ae3e
|
@ -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
|
||||
|
|
|
@ -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+
|
||||
|
|
|
@ -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
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
||||
|
|
@ -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 */
|
|
@ -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]++;
|
||||
}
|
|
@ -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 */
|
|
@ -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"
|
|
@ -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
|
Loading…
Reference in New Issue