/* * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #ifndef _SDFAT_H #define _SDFAT_H #include #include #include #include #include #include #include #include #include #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 common MACRO */ #define CLUSTER_16(x) ((u16)((x) & 0xFFFFU)) #define CLUSTER_32(x) ((u32)((x) & 0xFFFFFFFFU)) #define CLUS_EOF CLUSTER_32(~0) #define CLUS_FREE (0) #define CLUS_BASE (2) #define IS_CLUS_EOF(x) (x == CLUS_EOF) #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) uid_t fs_uid; gid_t fs_gid; #else kuid_t fs_uid; kgid_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,00) 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 ->i_mutex 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,00) 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 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/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 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); #endif /* sdfat/misc.c */ extern void __sdfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) __attribute__ ((format (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, ...) __attribute__ ((format (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_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) do {sdfat_time_current_usec(s); } while (0) #define TIME_END(e) do {sdfat_time_current_usec(e); } while (0) #define TIME_ELAPSED(s, e) ((u32)(((e)->tv_sec - (s)->tv_sec) * 1000000 + \ ((e)->tv_usec - (s)->tv_usec))) #define PRINT_TIME(n) \ do { \ printk("[SDFAT] Elapsed time %d = %d (usec)\n", \ n, (__t2 - __t1)); \ } while(0) #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, ...) __attribute__ ((format (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)) { \ printk(KERN_ERR "Assertion failed! %s\n", #expr); \ BUG_ON(1); \ } #endif /* !_SDFAT_H */