From ed48c1cb6f30942f0a27862567208430e978c693 Mon Sep 17 00:00:00 2001 From: "Kevin F. Haggerty" Date: Fri, 13 Dec 2019 16:38:33 -0700 Subject: [PATCH] fs: sdfat: Update to version 2.3.0 * Samsung version G975FXXU3BSKO Change-Id: I11a2c361ba70441d2a75188a4f91d3cd324d1a9e Signed-off-by: Kevin F. Haggerty --- fs/sdfat/Kconfig | 5 ++ fs/sdfat/api.h | 11 +++- fs/sdfat/core.c | 23 ++++++++- fs/sdfat/core_exfat.c | 10 +++- fs/sdfat/core_fat.c | 1 + fs/sdfat/misc.c | 90 +++++++++++++++++++++++++++++---- fs/sdfat/sdfat.c | 114 +++++++++++++++++++++++++++++------------- fs/sdfat/sdfat.h | 22 +++++++- fs/sdfat/sdfat_fs.h | 6 ++- fs/sdfat/version.h | 2 +- 10 files changed, 228 insertions(+), 56 deletions(-) diff --git a/fs/sdfat/Kconfig b/fs/sdfat/Kconfig index 3a15bbb5552..6c0d268dc83 100644 --- a/fs/sdfat/Kconfig +++ b/fs/sdfat/Kconfig @@ -120,3 +120,8 @@ config SDFAT_STATISTICS bool "enable statistics for bigdata" depends on SDFAT_FS default y + +config SDFAT_UEVENT + bool "Enable uevent" + depends on SDFAT_FS + default y diff --git a/fs/sdfat/api.h b/fs/sdfat/api.h index df6a32fb8b3..344297ab58a 100644 --- a/fs/sdfat/api.h +++ b/fs/sdfat/api.h @@ -98,6 +98,14 @@ typedef struct { /* Type Definitions */ /*----------------------------------------------------------------------*/ /* should be merged it to DATE_TIME_T */ +typedef union { + struct { + u8 off : 7; + u8 valid : 1; + }; + u8 value; +} TIMEZONE_T; + typedef struct { u16 sec; /* 0 ~ 59 */ u16 min; /* 0 ~ 59 */ @@ -105,9 +113,9 @@ typedef struct { u16 day; /* 1 ~ 31 */ u16 mon; /* 1 ~ 12 */ u16 year; /* 0 ~ 127 (since 1980) */ + TIMEZONE_T tz; } TIMESTAMP_T; - typedef struct { u16 Year; u16 Month; @@ -116,6 +124,7 @@ typedef struct { u16 Minute; u16 Second; u16 MilliSecond; + TIMEZONE_T Timezone; } DATE_TIME_T; typedef struct { diff --git a/fs/sdfat/core.c b/fs/sdfat/core.c index 618c9e7f72d..3a5af0b83d5 100644 --- a/fs/sdfat/core.c +++ b/fs/sdfat/core.c @@ -33,12 +33,27 @@ #include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +#include +#endif #include "sdfat.h" #include "core.h" #include #include + +/************************************************************************* + * FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY + *************************************************************************/ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) +static inline u64 inode_peek_iversion(struct inode *inode) +{ + return inode->i_version; +} +#endif + + /*----------------------------------------------------------------------*/ /* Constant & Macro Definitions */ /*----------------------------------------------------------------------*/ @@ -1956,10 +1971,10 @@ s32 fscore_lookup(struct inode *inode, u8 *path, FILE_ID_T *fid) return ret; /* check the validation of hint_stat and initialize it if required */ - if (dir_fid->version != (u32)(inode->i_version & 0xffffffff)) { + if (dir_fid->version != (u32)inode_peek_iversion(inode)) { dir_fid->hint_stat.clu = dir.dir; dir_fid->hint_stat.eidx = 0; - dir_fid->version = (u32)(inode->i_version & 0xffffffff); + dir_fid->version = (u32)inode_peek_iversion(inode); dir_fid->hint_femp.eidx = -1; } @@ -2956,6 +2971,7 @@ s32 fscore_read_inode(struct inode *inode, DIR_ENTRY_T *info) info->CreateTimestamp.Minute = tm.min; info->CreateTimestamp.Second = tm.sec; info->CreateTimestamp.MilliSecond = 0; + info->CreateTimestamp.Timezone.value = tm.tz.value; fsi->fs_func->get_entry_time(ep, &tm, TM_MODIFY); info->ModifyTimestamp.Year = tm.year; @@ -2965,6 +2981,7 @@ s32 fscore_read_inode(struct inode *inode, DIR_ENTRY_T *info) info->ModifyTimestamp.Minute = tm.min; info->ModifyTimestamp.Second = tm.sec; info->ModifyTimestamp.MilliSecond = 0; + info->ModifyTimestamp.Timezone.value = tm.tz.value; memset((s8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); @@ -3067,6 +3084,7 @@ s32 fscore_write_inode(struct inode *inode, DIR_ENTRY_T *info, s32 sync) fsi->fs_func->set_entry_attr(ep, info->Attr); /* set FILE_INFO structure using the acquired DENTRY_T */ + tm.tz = info->CreateTimestamp.Timezone; tm.sec = info->CreateTimestamp.Second; tm.min = info->CreateTimestamp.Minute; tm.hour = info->CreateTimestamp.Hour; @@ -3075,6 +3093,7 @@ s32 fscore_write_inode(struct inode *inode, DIR_ENTRY_T *info, s32 sync) tm.year = info->CreateTimestamp.Year; fsi->fs_func->set_entry_time(ep, &tm, TM_CREATE); + tm.tz = info->ModifyTimestamp.Timezone; tm.sec = info->ModifyTimestamp.Second; tm.min = info->ModifyTimestamp.Minute; tm.hour = info->ModifyTimestamp.Hour; diff --git a/fs/sdfat/core_exfat.c b/fs/sdfat/core_exfat.c index 08c45b09bb7..cde77205744 100644 --- a/fs/sdfat/core_exfat.c +++ b/fs/sdfat/core_exfat.c @@ -222,24 +222,28 @@ static void exfat_set_entry_size(DENTRY_T *p_entry, u64 size) static void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) { - u16 t = 0x00, d = 0x21; + u16 t = 0x00, d = 0x21, tz = 0x00; FILE_DENTRY_T *ep = (FILE_DENTRY_T *)p_entry; switch (mode) { case TM_CREATE: t = le16_to_cpu(ep->create_time); d = le16_to_cpu(ep->create_date); + tz = ep->create_tz; break; case TM_MODIFY: t = le16_to_cpu(ep->modify_time); d = le16_to_cpu(ep->modify_date); + tz = ep->modify_tz; break; case TM_ACCESS: t = le16_to_cpu(ep->access_time); d = le16_to_cpu(ep->access_date); + tz = ep->access_tz; break; } + tp->tz.value = tz; tp->sec = (t & 0x001F) << 1; tp->min = (t >> 5) & 0x003F; tp->hour = (t >> 11); @@ -260,14 +264,17 @@ static void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) case TM_CREATE: ep->create_time = cpu_to_le16(t); ep->create_date = cpu_to_le16(d); + ep->create_tz = tp->tz.value; break; case TM_MODIFY: ep->modify_time = cpu_to_le16(t); ep->modify_date = cpu_to_le16(d); + ep->modify_tz = tp->tz.value; break; case TM_ACCESS: ep->access_time = cpu_to_le16(t); ep->access_date = cpu_to_le16(d); + ep->access_tz = tp->tz.value; break; } } /* end of exfat_set_entry_time */ @@ -285,7 +292,6 @@ static void __init_file_entry(struct super_block *sb, FILE_DENTRY_T *ep, u32 typ exfat_set_entry_time((DENTRY_T *) ep, tp, TM_ACCESS); ep->create_time_ms = 0; ep->modify_time_ms = 0; - ep->access_time_ms = 0; } /* end of __init_file_entry */ static void __init_strm_entry(STRM_DENTRY_T *ep, u8 flags, u32 start_clu, u64 size) diff --git a/fs/sdfat/core_fat.c b/fs/sdfat/core_fat.c index d25ec32d3b3..5e0a196ae42 100644 --- a/fs/sdfat/core_fat.c +++ b/fs/sdfat/core_fat.c @@ -441,6 +441,7 @@ static void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) break; } + tp->tz.value = 0x00; tp->sec = (t & 0x001F) << 1; tp->min = (t >> 5) & 0x003F; tp->hour = (t >> 11); diff --git a/fs/sdfat/misc.c b/fs/sdfat/misc.c index 9b07d1dea43..ac1de710b58 100644 --- a/fs/sdfat/misc.c +++ b/fs/sdfat/misc.c @@ -55,11 +55,55 @@ /************************************************************************* * FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY *************************************************************************/ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) -#define CURRENT_TIME_SEC timespec_trunc(current_kernel_time(), NSEC_PER_SEC) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) +#define CURRENT_TIME_SEC timespec64_trunc(current_kernel_time64(), NSEC_PER_SEC) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) +#define CURRENT_TIME_SEC timespec_trunc(current_kernel_time(), NSEC_PER_SEC) +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) */ + /* EMPTY */ #endif +#ifdef CONFIG_SDFAT_UEVENT +static struct kobject sdfat_uevent_kobj; + +int sdfat_uevent_init(struct kset *sdfat_kset) +{ + int err; + struct kobj_type *ktype = get_ktype(&sdfat_kset->kobj); + + sdfat_uevent_kobj.kset = sdfat_kset; + err = kobject_init_and_add(&sdfat_uevent_kobj, ktype, NULL, "uevent"); + if (err) + pr_err("[SDFAT] Unable to create sdfat uevent kobj\n"); + + return err; +} + +void sdfat_uevent_uninit(void) +{ + kobject_del(&sdfat_uevent_kobj); + memset(&sdfat_uevent_kobj, 0, sizeof(struct kobject)); +} + +void sdfat_uevent_ro_remount(struct super_block *sb) +{ + struct block_device *bdev = sb->s_bdev; + dev_t bd_dev = bdev ? bdev->bd_dev : 0; + + char major[16], minor[16]; + char *envp[] = { major, minor, NULL }; + + snprintf(major, sizeof(major), "MAJOR=%d", MAJOR(bd_dev)); + snprintf(minor, sizeof(minor), "MINOR=%d", MINOR(bd_dev)); + + kobject_uevent_env(&sdfat_uevent_kobj, KOBJ_CHANGE, envp); + + ST_LOG("[SDFAT](%s[%d:%d]): Uevent triggered\n", + sb->s_id, MAJOR(bd_dev), MINOR(bd_dev)); +} +#endif + /* * sdfat_fs_error reports a file system problem that might indicate fa data * corruption/inconsistency. Depending on 'errors' mount option the @@ -103,6 +147,7 @@ void __sdfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) ST_LOG("[SDFAT](%s[%d:%d]): Filesystem has been set read-only\n", sb->s_id, MAJOR(bd_dev), MINOR(bd_dev)); #endif + sdfat_uevent_ro_remount(sb); } } EXPORT_SYMBOL(__sdfat_fs_error); @@ -178,9 +223,10 @@ static time_t accum_days_in_year[] = { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, }; +#define TIMEZONE_SEC(x) ((x) * 15 * SECS_PER_MIN) /* 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) +void sdfat_time_fat2unix(struct sdfat_sb_info *sbi, sdfat_timespec_t *ts, + DATE_TIME_T *tp) { time_t year = tp->Year; time_t ld; /* leap day */ @@ -195,22 +241,45 @@ void sdfat_time_fat2unix(struct sdfat_sb_info *sbi, struct timespec *ts, + (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_nsec = 0; + + /* Treat as local time */ + if (!sbi->options.tz_utc && !tp->Timezone.valid) { ts->tv_sec += sys_tz.tz_minuteswest * SECS_PER_MIN; + return; + } - ts->tv_nsec = 0; + /* Treat as UTC time */ + if (!tp->Timezone.valid) + return; + + /* Treat as UTC time, but need to adjust timezone to UTC0 */ + if (tp->Timezone.off <= 0x3F) + ts->tv_sec -= TIMEZONE_SEC(tp->Timezone.off); + else /* 0x40 <= (tp->Timezone & 0x7F) <=0x7F */ + ts->tv_sec += TIMEZONE_SEC(0x80 - tp->Timezone.off); } +#define TIMEZONE_CUR_OFFSET() ((sys_tz.tz_minuteswest / (-15)) & 0x7F) /* 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) +void sdfat_time_unix2fat(struct sdfat_sb_info *sbi, sdfat_timespec_t *ts, + DATE_TIME_T *tp) { + bool tz_valid = (sbi->fsi.vol_type == EXFAT) ? true : false; time_t second = ts->tv_sec; time_t day, month, year; time_t ld; /* leap day */ - if (!sbi->options.tz_utc) + tp->Timezone.value = 0x00; + + /* Treats as local time with proper time */ + if (tz_valid || !sbi->options.tz_utc) { second -= sys_tz.tz_minuteswest * SECS_PER_MIN; + if (tz_valid) { + tp->Timezone.valid = 1; + tp->Timezone.off = TIMEZONE_CUR_OFFSET(); + } + } /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ if (second < UNIX_SECS_1980) { @@ -266,7 +335,7 @@ void sdfat_time_unix2fat(struct sdfat_sb_info *sbi, struct timespec *ts, TIMESTAMP_T *tm_now(struct sdfat_sb_info *sbi, TIMESTAMP_T *tp) { - struct timespec ts = CURRENT_TIME_SEC; + sdfat_timespec_t ts = CURRENT_TIME_SEC; DATE_TIME_T dt; sdfat_time_unix2fat(sbi, &ts, &dt); @@ -277,6 +346,7 @@ TIMESTAMP_T *tm_now(struct sdfat_sb_info *sbi, TIMESTAMP_T *tp) tp->hour = dt.Hour; tp->min = dt.Minute; tp->sec = dt.Second; + tp->tz.value = dt.Timezone.value; return tp; } diff --git a/fs/sdfat/sdfat.c b/fs/sdfat/sdfat.c index 0bf64ad20f6..65fcbf7e5ad 100644 --- a/fs/sdfat/sdfat.c +++ b/fs/sdfat/sdfat.c @@ -54,7 +54,9 @@ #include #include #include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +#include +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) #include #endif @@ -138,6 +140,18 @@ static int __sdfat_cmpi(const struct dentry *dentry, unsigned int len, /************************************************************************* * FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY *************************************************************************/ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) +static inline void inode_set_iversion(struct inode *inode, u64 val) +{ + inode->i_version = val; +} +static inline u64 inode_peek_iversion(struct inode *inode) +{ + return inode->i_version; +} +#endif + + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) /* EMPTY */ #else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) */ @@ -148,10 +162,6 @@ static inline void bio_set_dev(struct bio *bio, struct block_device *bdev) #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) -#define CURRENT_TIME_SEC timespec_trunc(current_kernel_time(), NSEC_PER_SEC) -#endif - #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) static int sdfat_getattr(const struct path *path, struct kstat *stat, u32 request_mask, unsigned int query_flags) @@ -894,6 +904,15 @@ static int sdfat_file_fsync(struct file *filp, int datasync) /************************************************************************* * MORE FUNCTIONS WHICH HAS KERNEL VERSION DEPENDENCY *************************************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) +#define CURRENT_TIME_SEC timespec64_trunc(current_kernel_time64(), NSEC_PER_SEC) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) +#define CURRENT_TIME_SEC timespec_trunc(current_kernel_time(), NSEC_PER_SEC) +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) */ + /* EMPTY */ +#endif + + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) static void sdfat_writepage_end_io(struct bio *bio) { @@ -1220,7 +1239,8 @@ static int __sdfat_revalidate_common(struct dentry *dentry) spin_lock(&dentry->d_lock); if ((!dentry->d_inode) && (!__check_dstate_locked(dentry) && - (dentry->d_time != dentry->d_parent->d_inode->i_version))) { + (dentry->d_time != + (unsigned long)inode_peek_iversion(dentry->d_parent->d_inode)))) { ret = 0; } spin_unlock(&dentry->d_lock); @@ -2368,7 +2388,7 @@ static int __sdfat_create(struct inode *dir, struct dentry *dentry) { struct super_block *sb = dir->i_sb; struct inode *inode; - struct timespec ts; + sdfat_timespec_t ts; FILE_ID_T fid; loff_t i_pos; int err; @@ -2385,7 +2405,7 @@ static int __sdfat_create(struct inode *dir, struct dentry *dentry) __lock_d_revalidate(dentry); - dir->i_version++; + inode_inc_iversion(dir); dir->i_ctime = dir->i_mtime = dir->i_atime = ts; if (IS_DIRSYNC(dir)) (void) sdfat_sync_inode(dir); @@ -2398,7 +2418,7 @@ static int __sdfat_create(struct inode *dir, struct dentry *dentry) err = PTR_ERR(inode); goto out; } - inode->i_version++; + inode_inc_iversion(inode); inode->i_mtime = inode->i_atime = inode->i_ctime = ts; /* timestamp is already written, so mark_inode_dirty() is unneeded. */ @@ -2515,7 +2535,7 @@ static struct dentry *__sdfat_lookup(struct inode *dir, struct dentry *dentry) dput(alias); out: /* initialize d_time even though it is positive dentry */ - dentry->d_time = dir->i_version; + dentry->d_time = (unsigned long)inode_peek_iversion(dir); __unlock_super(sb); dentry = d_splice_alias(inode, dentry); @@ -2533,7 +2553,7 @@ static int sdfat_unlink(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; struct super_block *sb = dir->i_sb; - struct timespec ts; + sdfat_timespec_t ts; int err; __lock_super(sb); @@ -2552,7 +2572,7 @@ static int sdfat_unlink(struct inode *dir, struct dentry *dentry) __lock_d_revalidate(dentry); - dir->i_version++; + inode_inc_iversion(dir); dir->i_mtime = dir->i_atime = ts; if (IS_DIRSYNC(dir)) (void) sdfat_sync_inode(dir); @@ -2562,7 +2582,7 @@ static int sdfat_unlink(struct inode *dir, struct dentry *dentry) clear_nlink(inode); inode->i_mtime = inode->i_atime = ts; sdfat_detach(inode); - dentry->d_time = dir->i_version; + dentry->d_time = (unsigned long)inode_peek_iversion(dir); out: __unlock_d_revalidate(dentry); __unlock_super(sb); @@ -2574,7 +2594,7 @@ static int sdfat_symlink(struct inode *dir, struct dentry *dentry, const char *t { struct super_block *sb = dir->i_sb; struct inode *inode; - struct timespec ts; + sdfat_timespec_t ts; FILE_ID_T fid; loff_t i_pos; int err; @@ -2604,7 +2624,7 @@ static int sdfat_symlink(struct inode *dir, struct dentry *dentry, const char *t __lock_d_revalidate(dentry); - dir->i_version++; + inode_inc_iversion(dir); dir->i_ctime = dir->i_mtime = dir->i_atime = ts; if (IS_DIRSYNC(dir)) (void) sdfat_sync_inode(dir); @@ -2617,7 +2637,7 @@ static int sdfat_symlink(struct inode *dir, struct dentry *dentry, const char *t err = PTR_ERR(inode); goto out; } - inode->i_version++; + inode_inc_iversion(inode); inode->i_mtime = inode->i_atime = inode->i_ctime = ts; /* timestamp is already written, so mark_inode_dirty() is unneeded. */ @@ -2641,7 +2661,7 @@ static int __sdfat_mkdir(struct inode *dir, struct dentry *dentry) { struct super_block *sb = dir->i_sb; struct inode *inode; - struct timespec ts; + sdfat_timespec_t ts; FILE_ID_T fid; loff_t i_pos; int err; @@ -2658,7 +2678,7 @@ static int __sdfat_mkdir(struct inode *dir, struct dentry *dentry) __lock_d_revalidate(dentry); - dir->i_version++; + inode_inc_iversion(dir); dir->i_ctime = dir->i_mtime = dir->i_atime = ts; if (IS_DIRSYNC(dir)) (void) sdfat_sync_inode(dir); @@ -2672,7 +2692,7 @@ static int __sdfat_mkdir(struct inode *dir, struct dentry *dentry) err = PTR_ERR(inode); goto out; } - inode->i_version++; + inode_inc_iversion(inode); inode->i_mtime = inode->i_atime = inode->i_ctime = ts; /* timestamp is already written, so mark_inode_dirty() is unneeded. */ @@ -2692,7 +2712,7 @@ static int sdfat_rmdir(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; struct super_block *sb = dir->i_sb; - struct timespec ts; + sdfat_timespec_t ts; int err; __lock_super(sb); @@ -2709,7 +2729,7 @@ static int sdfat_rmdir(struct inode *dir, struct dentry *dentry) __lock_d_revalidate(dentry); - dir->i_version++; + inode_inc_iversion(dir); dir->i_mtime = dir->i_atime = ts; if (IS_DIRSYNC(dir)) (void) sdfat_sync_inode(dir); @@ -2720,7 +2740,7 @@ static int sdfat_rmdir(struct inode *dir, struct dentry *dentry) clear_nlink(inode); inode->i_mtime = inode->i_atime = ts; sdfat_detach(inode); - dentry->d_time = dir->i_version; + dentry->d_time = (unsigned long)inode_peek_iversion(dir); out: __unlock_d_revalidate(dentry); __unlock_super(sb); @@ -2733,7 +2753,7 @@ static int __sdfat_rename(struct inode *old_dir, struct dentry *old_dentry, { struct inode *old_inode, *new_inode; struct super_block *sb = old_dir->i_sb; - struct timespec ts; + sdfat_timespec_t ts; loff_t i_pos; int err; @@ -2757,7 +2777,7 @@ static int __sdfat_rename(struct inode *old_dir, struct dentry *old_dentry, __lock_d_revalidate(old_dentry); __lock_d_revalidate(new_dentry); - new_dir->i_version++; + inode_inc_iversion(new_dir); new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime = ts; if (IS_DIRSYNC(new_dir)) (void) sdfat_sync_inode(new_dir); @@ -2778,7 +2798,7 @@ static int __sdfat_rename(struct inode *old_dir, struct dentry *old_dentry, inc_nlink(new_dir); } - old_dir->i_version++; + inode_inc_iversion(old_dir); old_dir->i_ctime = old_dir->i_mtime = ts; if (IS_DIRSYNC(old_dir)) (void) sdfat_sync_inode(old_dir); @@ -2887,6 +2907,18 @@ static int sdfat_sanitize_mode(const struct sdfat_sb_info *sbi, return 0; } +/* + * sdfat_block_truncate_page() zeroes out a mapping from file offset `from' + * up to the end of the block which corresponds to `from'. + * This is required during truncate to physically zeroout the tail end + * of that block so it doesn't yield old data if the file is later grown. + * Also, avoid causing failure from fsx for cases of "data past EOF" + */ +static int sdfat_block_truncate_page(struct inode *inode, loff_t from) +{ + return block_truncate_page(inode->i_mapping, from, sdfat_get_block); +} + static int sdfat_setattr(struct dentry *dentry, struct iattr *attr) { @@ -2902,7 +2934,7 @@ static int sdfat_setattr(struct dentry *dentry, struct iattr *attr) && (attr->ia_size > i_size_read(inode))) { error = sdfat_cont_expand(inode, attr->ia_size); if (error || attr->ia_valid == ATTR_SIZE) - return error; + goto out; attr->ia_valid &= ~ATTR_SIZE; } @@ -2916,7 +2948,7 @@ static int sdfat_setattr(struct dentry *dentry, struct iattr *attr) error = setattr_prepare(dentry, attr); attr->ia_valid = ia_valid; if (error) - return error; + goto out; if (((attr->ia_valid & ATTR_UID) && (!uid_eq(attr->ia_uid, sbi->options.fs_uid))) || @@ -2924,7 +2956,8 @@ static int sdfat_setattr(struct dentry *dentry, struct iattr *attr) (!gid_eq(attr->ia_gid, sbi->options.fs_gid))) || ((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | S_IRWXUGO)))) { - return -EPERM; + error = -EPERM; + goto out; } /* @@ -2940,6 +2973,10 @@ static int sdfat_setattr(struct dentry *dentry, struct iattr *attr) /* patch 1.2.0 : fixed the problem of size mismatch. */ if (attr->ia_valid & ATTR_SIZE) { + error = sdfat_block_truncate_page(inode, attr->ia_size); + if (error) + goto out; + old_size = i_size_read(inode); /* TO CHECK evicting directory works correctly */ @@ -2949,8 +2986,7 @@ static int sdfat_setattr(struct dentry *dentry, struct iattr *attr) } setattr_copy(inode, attr); mark_inode_dirty(inode); - - +out: TMSG("%s exited with err(%d)\n", __func__, error); return error; } @@ -3899,7 +3935,7 @@ static int sdfat_fill_inode(struct inode *inode, const FILE_ID_T *fid) SDFAT_I(inode)->target = NULL; inode->i_uid = sbi->options.fs_uid; inode->i_gid = sbi->options.fs_gid; - inode->i_version++; + inode_inc_iversion(inode); inode->i_generation = get_seconds(); if (fsapi_read_inode(inode, &info) < 0) { @@ -3977,7 +4013,7 @@ static struct inode *sdfat_build_inode(struct super_block *sb, goto out; } inode->i_ino = iunique(sb, SDFAT_ROOT_INO); - inode->i_version = 1; + inode_set_iversion(inode, 1); err = sdfat_fill_inode(inode, fid); if (err) { iput(inode); @@ -4797,7 +4833,7 @@ static int sdfat_read_root(struct inode *inode) { struct super_block *sb = inode->i_sb; struct sdfat_sb_info *sbi = SDFAT_SB(sb); - struct timespec ts; + sdfat_timespec_t ts; FS_INFO_T *fsi = &(sbi->fsi); DIR_ENTRY_T info; @@ -4823,7 +4859,7 @@ static int sdfat_read_root(struct inode *inode) inode->i_uid = sbi->options.fs_uid; inode->i_gid = sbi->options.fs_gid; - inode->i_version++; + inode_inc_iversion(inode); inode->i_generation = 0; inode->i_mode = sdfat_make_mode(sbi, ATTR_SUBDIR, S_IRWXUGO); inode->i_op = &sdfat_dir_inode_operations; @@ -4947,7 +4983,7 @@ static int sdfat_fill_super(struct super_block *sb, void *data, int silent) } root_inode->i_ino = SDFAT_ROOT_INO; - root_inode->i_version = 1; + inode_set_iversion(root_inode, 1); err = sdfat_read_root(root_inode); if (err) { @@ -5117,7 +5153,7 @@ static int __init init_sdfat_fs(void) sdfat_kset = kset_create_and_add("sdfat", NULL, fs_kobj); if (!sdfat_kset) { - pr_err("[SDFAT] failed to create fs_kobj\n"); + pr_err("[SDFAT] failed to create sdfat kset\n"); err = -ENOMEM; goto error; } @@ -5132,6 +5168,10 @@ static int __init init_sdfat_fs(void) if (err) goto error; + err = sdfat_uevent_init(sdfat_kset); + if (err) + goto error; + err = sdfat_init_inodecache(); if (err) { pr_err("[SDFAT] failed to initialize inode cache\n"); @@ -5162,6 +5202,7 @@ static int __init init_sdfat_fs(void) return 0; error: + sdfat_uevent_uninit(); sdfat_statistics_uninit(); if (sdfat_kset) { @@ -5179,6 +5220,7 @@ error: static void __exit exit_sdfat_fs(void) { + sdfat_uevent_uninit(); sdfat_statistics_uninit(); if (sdfat_kset) { diff --git a/fs/sdfat/sdfat.h b/fs/sdfat/sdfat.h index 5ac46598a7b..60f7811c7b9 100644 --- a/fs/sdfat/sdfat.h +++ b/fs/sdfat/sdfat.h @@ -209,6 +209,12 @@ struct sdfat_inode_info { struct inode vfs_inode; }; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) +typedef struct timespec64 sdfat_timespec_t; +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0) */ +typedef struct timespec sdfat_timespec_t; +#endif + /* * FIXME : needs on-disk-slot in-memory data */ @@ -350,6 +356,18 @@ static inline void setup_sdfat_xattr_handler(struct super_block *sb) {}; #endif /* sdfat/misc.c */ +#ifdef CONFIG_SDFAT_UEVENT +extern int sdfat_uevent_init(struct kset *sdfat_kset); +extern void sdfat_uevent_uninit(void); +extern void sdfat_uevent_ro_remount(struct super_block *sb); +#else +static inline int sdfat_uevent_init(struct kset *sdfat_kset) +{ + return 0; +} +static inline void sdfat_uevent_uninit(void) {}; +static inline void sdfat_uevent_ro_remount(struct super_block *sb) {}; +#endif extern void __sdfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) __printf(3, 4) __cold; @@ -365,9 +383,9 @@ __sdfat_msg(struct super_block *sb, const char *lv, int st, const char *fmt, ... #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, +extern void sdfat_time_fat2unix(struct sdfat_sb_info *sbi, sdfat_timespec_t *ts, DATE_TIME_T *tp); -extern void sdfat_time_unix2fat(struct sdfat_sb_info *sbi, struct timespec *ts, +extern void sdfat_time_unix2fat(struct sdfat_sb_info *sbi, sdfat_timespec_t *ts, DATE_TIME_T *tp); extern TIMESTAMP_T *tm_now(struct sdfat_sb_info *sbi, TIMESTAMP_T *tm); diff --git a/fs/sdfat/sdfat_fs.h b/fs/sdfat/sdfat_fs.h index 487ac3f8af3..b012e406fb2 100644 --- a/fs/sdfat/sdfat_fs.h +++ b/fs/sdfat/sdfat_fs.h @@ -363,8 +363,10 @@ typedef struct { __le16 access_date; // aligned __u8 create_time_ms; __u8 modify_time_ms; - __u8 access_time_ms; - __u8 reserved2[9]; + __u8 create_tz; + __u8 modify_tz; + __u8 access_tz; + __u8 reserved2[7]; } FILE_DENTRY_T; /* EXFAT stream extension directory entry (32 bytes) */ diff --git a/fs/sdfat/version.h b/fs/sdfat/version.h index 3cd0b73691a..44e44e03d84 100644 --- a/fs/sdfat/version.h +++ b/fs/sdfat/version.h @@ -22,4 +22,4 @@ /* PURPOSE : sdFAT File Manager */ /* */ /************************************************************************/ -#define SDFAT_VERSION "2.1.8-lineage" +#define SDFAT_VERSION "2.3.0-lineage" -- 2.20.1