From dc9361e2723f58448ba59a22b3c92f27781e772c Mon Sep 17 00:00:00 2001 From: payton Date: Tue, 5 Dec 2023 11:30:03 +0800 Subject: [PATCH] =?UTF-8?q?1.=E4=BF=AE=E5=A4=8Dexfat=E5=8D=A1=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E7=B3=BB=E7=BB=9F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- code/driver/source/fs/exfat/Makefile | 1 + code/driver/source/fs/exfat/exfat_config.h | 4 + code/driver/source/fs/exfat/exfat_core.c | 8 + code/driver/source/fs/exfat/exfat_oal.c | 4 +- code/driver/source/fs/exfat/exfat_oal.h | 7 + code/driver/source/fs/exfat/exfat_super.c | 237 +++++++++++++++++++-- code/driver/source/fs/exfat/exfat_super.h | 26 +++ 7 files changed, 262 insertions(+), 25 deletions(-) diff --git a/code/driver/source/fs/exfat/Makefile b/code/driver/source/fs/exfat/Makefile index 52bcee4b9..272257a09 100755 --- a/code/driver/source/fs/exfat/Makefile +++ b/code/driver/source/fs/exfat/Makefile @@ -35,3 +35,4 @@ clean: .PHONY: modules modules_install clean endif + diff --git a/code/driver/source/fs/exfat/exfat_config.h b/code/driver/source/fs/exfat/exfat_config.h index 33c6525e4..63e8a79d8 100755 --- a/code/driver/source/fs/exfat/exfat_config.h +++ b/code/driver/source/fs/exfat/exfat_config.h @@ -66,4 +66,8 @@ #define CONFIG_EXFAT_DEFAULT_IOCHARSET "utf8" #endif +#ifndef FSLINUX_IOCTL_ENABLE +#define FSLINUX_IOCTL_ENABLE 1 /* to speedup by fslinux ioctl */ +#endif + #endif /* _EXFAT_CONFIG_H */ diff --git a/code/driver/source/fs/exfat/exfat_core.c b/code/driver/source/fs/exfat/exfat_core.c index 143b72155..08e0a9268 100755 --- a/code/driver/source/fs/exfat/exfat_core.c +++ b/code/driver/source/fs/exfat/exfat_core.c @@ -970,6 +970,9 @@ s32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid) CHAIN_T dir, clu_to_free; DENTRY_T *ep; struct super_block *sb = inode->i_sb; +#ifdef FSLINUX_IOCTL_ENABLE + struct exfat_sb_info *sbi = EXFAT_SB(sb); +#endif FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); dir.dir = fid->dir.dir; @@ -994,6 +997,11 @@ s32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid) clu_to_free.size = (s32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; clu_to_free.flags = fid->flags; +#ifdef FSLINUX_IOCTL_ENABLE + if (!EXFAT_IS_DELAY_SYNC(sbi, inode)) +#endif + ffsSyncVol(sb, 1); + /* (2) free the clusters */ p_fs->fs_func->free_cluster(sb, &clu_to_free, 0); diff --git a/code/driver/source/fs/exfat/exfat_oal.c b/code/driver/source/fs/exfat/exfat_oal.c index 743544244..a4017f8bb 100755 --- a/code/driver/source/fs/exfat/exfat_oal.c +++ b/code/driver/source/fs/exfat/exfat_oal.c @@ -128,13 +128,13 @@ static time_t accum_days_in_year[] = { TIMESTAMP_T *tm_current(TIMESTAMP_T *tp) { - struct timespec ts; + struct timespec_compat ts; time_t second, day, leap_day, month, year; #if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0) ts = CURRENT_TIME_SEC; #else - ktime_get_real_ts(&ts); + KTIME_GET_REAL_TS(&ts); #endif second = ts.tv_sec; diff --git a/code/driver/source/fs/exfat/exfat_oal.h b/code/driver/source/fs/exfat/exfat_oal.h index b6dd7897a..4ee0bfca8 100755 --- a/code/driver/source/fs/exfat/exfat_oal.h +++ b/code/driver/source/fs/exfat/exfat_oal.h @@ -51,6 +51,13 @@ /*----------------------------------------------------------------------*/ /* Type Definitions */ /*----------------------------------------------------------------------*/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) +#define timespec_compat timespec64 +#define KTIME_GET_REAL_TS ktime_get_real_ts64 +#else +#define timespec_compat timespec +#define KTIME_GET_REAL_TS ktime_get_real_ts +#endif typedef struct { u16 sec; /* 0 ~ 59 */ diff --git a/code/driver/source/fs/exfat/exfat_super.c b/code/driver/source/fs/exfat/exfat_super.c index 148d29d66..8ce759b4b 100755 --- a/code/driver/source/fs/exfat/exfat_super.c +++ b/code/driver/source/fs/exfat/exfat_super.c @@ -63,9 +63,6 @@ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) #include #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) -#include -#endif #include #include #include @@ -102,17 +99,6 @@ extern struct timezone sys_tz; #define current_time(x) (CURRENT_TIME_SEC) #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) -#define USE_NEW_IVERSION_API -#define INC_IVERSION(x) (inode_inc_iversion(x)) -#define GET_IVERSION(x) (inode_peek_iversion_raw(x)) -#define SET_IVERSION(x,y) (inode_set_iversion(x, y)) -#else -#define INC_IVERSION(x) (x->i_version++) -#define GET_IVERSION(x) (x->i_version) -#define SET_IVERSION(x,y) (x->i_version = y) -#endif - #define CHECK_ERR(x) BUG_ON(x) #define UNIX_SECS_1980 315532800L @@ -147,7 +133,8 @@ static time_t accum_days_in_year[] = { static void _exfat_truncate(struct inode *inode, loff_t old_size); /* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ -void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec64 *ts,DATE_TIME_T *tp) +void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec_compat *ts, + DATE_TIME_T *tp) { time_t year = tp->Year; time_t ld; @@ -176,7 +163,7 @@ void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec64 *ts,DATE_T } /* Convert linear UNIX date to a FAT time/date pair. */ -void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec64 *ts, +void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec_compat *ts, DATE_TIME_T *tp) { time_t second = ts->tv_sec; @@ -651,6 +638,126 @@ static int exfat_ioctl_volume_id(struct inode *dir) return p_fs->vol_id; } +#ifdef FSLINUX_IOCTL_ENABLE +static int exfat_ioctl_set_delay_sync(struct inode *dir, unsigned long arg) +{ + struct super_block *sb = dir->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + __lock_super(sb); + if (arg) { + sbi->delay_sync.is_delay_sync = 1; + sbi->delay_sync.dir_inode = dir; + } else { + sbi->delay_sync.is_delay_sync = 0; + sbi->delay_sync.dir_inode = NULL; + } + __unlock_super(sb); + + return 0; +} + +//Duplicated from exfat_readdir and modified it for EXFAT_IOCTL_GET_ENTRY_DATA +static int exfat_ioctl_readdir(struct file *filp, void *dirent) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0) + struct inode *inode = file_inode(filp); +#else + struct inode *inode = filp->f_path.dentry->d_inode; +#endif + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + DIR_ENTRY_T de; + unsigned long inum; + loff_t cpos; + int err = 0; + + __lock_super(sb); + + cpos = filp->f_pos; + + /* Fake . and .. for the root directory. */ + if ((p_fs->vol_type == EXFAT) || (inode->i_ino == EXFAT_ROOT_INO)) { + while (cpos < 2) { + if (inode->i_ino == EXFAT_ROOT_INO) + inum = EXFAT_ROOT_INO; //root folder + else if (cpos == 0) + inum = inode->i_ino; //current folder + else /* (cpos == 1) */ + inum = parent_ino(filp->f_path.dentry); //parent folder + + cpos++; + filp->f_pos++; + } + if (cpos == 2) + cpos = 0; + } + if (cpos & (DENTRY_SIZE - 1)) { + err = -ENOENT; + goto out; + } + + EXFAT_I(inode)->fid.size = i_size_read(inode); + EXFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS; + + err = FsReadDir(inode, &de); + if (err) { + /* at least we tried to read a sector + * move cpos to next sector position (should be aligned) + */ + if (err == FFS_MEDIAERR) { + cpos += 1 << p_bd->sector_size_bits; + cpos &= ~((1 << p_bd->sector_size_bits)-1); + } + + err = -EIO; + goto end_of_dir; + } + + cpos = EXFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS; + + if (!de.Name[0]) { + if (copy_to_user(dirent, &de, sizeof(DIR_ENTRY_T))) { + err = -EFAULT; + goto out; + } + goto end_of_dir; + } + + if (!memcmp(de.ShortName, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = inode->i_ino; + } else if (!memcmp(de.ShortName, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = parent_ino(filp->f_path.dentry); + } else { + loff_t i_pos = ((loff_t) EXFAT_I(inode)->fid.start_clu << 32) | + ((EXFAT_I(inode)->fid.rwoffset-1) & 0xffffffff); + struct inode *tmp = exfat_iget(sb, i_pos); + if (tmp) { + inum = tmp->i_ino; + iput(tmp); + } else { + inum = iunique(sb, EXFAT_ROOT_INO); + } + } + + if (copy_to_user(dirent, &de, sizeof(DIR_ENTRY_T))) { + err = -EFAULT; + goto out; + } + + filp->f_pos = cpos; + +end_of_dir: + filp->f_pos = cpos; + +out: + __unlock_super(sb); + return err; +} +#endif /* FSLINUX_IOCTL_ENABLE */ + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) static int exfat_generic_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) @@ -698,11 +805,26 @@ static long exfat_generic_ioctl(struct file *filp, return 0; } #endif /* CONFIG_EXFAT_KERNEL_DEBUG */ +#ifdef FSLINUX_IOCTL_ENABLE + case EXFAT_IOCTL_READDIR_DIRECT: { + int ret = -ENOENT; + + inode_lock(inode); + if (!IS_DEADDIR(inode)) + ret = exfat_ioctl_readdir(filp, (void *)arg); + inode_unlock(inode); + + return ret; + } + case EXFAT_IOCTL_SET_DELAY_SYNC: + return exfat_ioctl_set_delay_sync(inode, arg); +#endif /* FSLINUX_IOCTL_ENABLE */ default: return -ENOTTY; /* Inappropriate ioctl for device */ } } +#if 0 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) static int exfat_file_fsync(struct file *filp, struct dentry *dentry, @@ -725,6 +847,19 @@ static int exfat_file_fsync(struct file *filp, int datasync) return res ? res : err; } #endif +#else +int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) +{ + struct inode *inode = filp->f_mapping->host; + struct super_block *sb = inode->i_sb; + int res, err; + + res = generic_file_fsync(filp, start, end, datasync); + err = FsSyncVol(sb, 1); + + return res ? res : err; +} +#endif const struct file_operations exfat_dir_operations = { .llseek = generic_file_llseek, @@ -734,6 +869,7 @@ const struct file_operations exfat_dir_operations = { #else .readdir = exfat_readdir, #endif +#if 0 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) .ioctl = exfat_generic_ioctl, .fsync = exfat_file_fsync, @@ -741,6 +877,10 @@ const struct file_operations exfat_dir_operations = { .unlocked_ioctl = exfat_generic_ioctl, .fsync = generic_file_fsync, #endif +#else + .unlocked_ioctl = exfat_generic_ioctl, + .fsync = exfat_file_fsync, +#endif }; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) @@ -911,6 +1051,9 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; struct super_block *sb = dir->i_sb; +#ifdef FSLINUX_IOCTL_ENABLE + struct exfat_sb_info *sbi = EXFAT_SB(sb); +#endif /* FSLINUX_IOCTL_ENABLE */ int err; __lock_super(sb); @@ -929,7 +1072,11 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry) } INC_IVERSION(dir); dir->i_mtime = dir->i_atime = current_time(dir); +#ifdef FSLINUX_IOCTL_ENABLE + if (!EXFAT_IS_DELAY_SYNC(sbi, dir) && IS_DIRSYNC(dir)) +#else if (IS_DIRSYNC(dir)) +#endif /* FSLINUX_IOCTL_ENABLE */ (void) exfat_sync_inode(dir); else mark_inode_dirty(dir); @@ -1080,6 +1227,9 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; struct super_block *sb = dir->i_sb; +#ifdef FSLINUX_IOCTL_ENABLE + struct exfat_sb_info *sbi = EXFAT_SB(sb); +#endif /* FSLINUX_IOCTL_ENABLE */ int err; __lock_super(sb); @@ -1104,6 +1254,13 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry) } INC_IVERSION(dir); dir->i_mtime = dir->i_atime = current_time(dir); +#ifdef FSLINUX_IOCTL_ENABLE + if (EXFAT_IS_DELAY_SYNC(sbi, inode)) { + sbi->delay_sync.is_delay_sync = 0; + sbi->delay_sync.dir_inode = NULL; + (void) exfat_sync_inode(dir); + } else +#endif /* FSLINUX_IOCTL_ENABLE */ if (IS_DIRSYNC(dir)) (void) exfat_sync_inode(dir); else @@ -1187,6 +1344,7 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, if (!new_inode) inc_nlink(new_dir); } + INC_IVERSION(old_dir); old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir); if (IS_DIRSYNC(old_dir)) @@ -1479,12 +1637,17 @@ const struct file_operations exfat_file_operations = { #endif .mmap = generic_file_mmap, .release = exfat_file_release, +#if 0 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) .ioctl = exfat_generic_ioctl, .fsync = exfat_file_fsync, #else .unlocked_ioctl = exfat_generic_ioctl, .fsync = generic_file_fsync, +#endif +#else + .unlocked_ioctl = exfat_generic_ioctl, + .fsync = exfat_file_fsync, #endif .splice_read = generic_file_splice_read, }; @@ -1724,9 +1887,6 @@ static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t offset, unsigned long nr_segs) #endif -#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) -static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, - struct iov_iter *iter, loff_t offset) #elif LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset) @@ -1739,7 +1899,7 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter) struct address_space *mapping = iocb->ki_filp->f_mapping; #endif ssize_t ret; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,2,0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) int rw; rw = iov_iter_rw(iter); @@ -1941,8 +2101,6 @@ static int exfat_fill_inode(struct inode *inode, FILE_ID_T *fid) inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) & ~((loff_t)p_fs->cluster_size - 1)) >> 9; - - exfat_time_fat2unix(sbi, &inode->i_mtime, &info.ModifyTimestamp); exfat_time_fat2unix(sbi, &inode->i_ctime, &info.CreateTimestamp); exfat_time_fat2unix(sbi, &inode->i_atime, &info.AccessTimestamp); @@ -1977,13 +2135,41 @@ out: return inode; } +static int __exfat_write_inode(struct inode *inode, int wait) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + DIR_ENTRY_T info; + int err = FFS_SUCCESS; + + if (inode->i_ino != EXFAT_ROOT_INO) { + info.Attr = exfat_make_attr(inode); + info.Size = i_size_read(inode); + + exfat_time_unix2fat(sbi, &inode->i_mtime, &info.ModifyTimestamp); + exfat_time_unix2fat(sbi, &inode->i_ctime, &info.CreateTimestamp); + exfat_time_unix2fat(sbi, &inode->i_atime, &info.AccessTimestamp); + + FsWriteStat(inode, &info); + } + + if (wait) + err = FsSyncVol(sb, 1); + + return err; +} + static int exfat_sync_inode(struct inode *inode) { +#if 0 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34) return exfat_write_inode(inode, 0); #else return exfat_write_inode(inode, NULL); #endif +#else + return __exfat_write_inode(inode, 1); +#endif } static struct inode *exfat_alloc_inode(struct super_block *sb) @@ -2016,6 +2202,7 @@ static int exfat_write_inode(struct inode *inode, int wait) static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) #endif { +#if 0 struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); DIR_ENTRY_T info; @@ -2033,6 +2220,11 @@ static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) FsWriteStat(inode, &info); return 0; +#else + if (inode->i_ino == EXFAT_ROOT_INO) + return 0; + return __exfat_write_inode(inode, 0);; +#endif } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) @@ -2586,7 +2778,6 @@ static int exfat_fill_super(struct super_block *sb, void *data, int silent) goto out_fail2; root_inode->i_ino = EXFAT_ROOT_INO; SET_IVERSION(root_inode, 1); - error = exfat_read_root(root_inode); if (error < 0) goto out_fail2; diff --git a/code/driver/source/fs/exfat/exfat_super.h b/code/driver/source/fs/exfat/exfat_super.h index 46a26ba04..8ffb0c765 100755 --- a/code/driver/source/fs/exfat/exfat_super.h +++ b/code/driver/source/fs/exfat/exfat_super.h @@ -38,6 +38,17 @@ #include "exfat_api.h" #include "exfat_core.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +#include +#define INC_IVERSION(x) (inode_inc_iversion(x)) +#define GET_IVERSION(x) (inode_peek_iversion_raw(x)) +#define SET_IVERSION(x,y) (inode_set_iversion(x, y)) +#else +#define INC_IVERSION(x) (x->i_version++) +#define GET_IVERSION(x) (x->i_version) +#define SET_IVERSION(x,y) (x->i_version = y) +#endif + #define EXFAT_ERRORS_CONT 1 /* ignore error and continue */ #define EXFAT_ERRORS_PANIC 2 /* panic on error */ #define EXFAT_ERRORS_RO 3 /* remount r/o on error */ @@ -45,6 +56,18 @@ /* ioctl command */ #define EXFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32) +#ifdef FSLINUX_IOCTL_ENABLE +#define EXFAT_IOCTL_READDIR_DIRECT _IOR('r', 0x13, DIR_ENTRY_T) +#define EXFAT_IOCTL_SET_DELAY_SYNC _IOR('r', 0x14, __u32) + +typedef struct { + unsigned long is_delay_sync; + struct inode *dir_inode; +} EXFAT_DELAY_SYNC; + +#define EXFAT_IS_DELAY_SYNC(sbi, node) (sbi->delay_sync.is_delay_sync && node == sbi->delay_sync.dir_inode) +#endif /* FSLINUX_IOCTL_ENABLE */ + struct exfat_mount_options { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) kuid_t fs_uid; @@ -93,6 +116,9 @@ struct exfat_sb_info { #ifdef CONFIG_EXFAT_KERNEL_DEBUG long debug_flags; #endif /* CONFIG_EXFAT_KERNEL_DEBUG */ +#ifdef FSLINUX_IOCTL_ENABLE + EXFAT_DELAY_SYNC delay_sync; +#endif /* FSLINUX_IOCTL_ENABLE */ }; /*