diff --git a/fs/sdfat/Kconfig b/fs/sdfat/Kconfig index 81cf1ddb688c..082c966d8c59 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 df6a32fb8b3e..344297ab58ae 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 0b02ee72ecca..069677fa6e3b 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)(GET_IVERSION(inode) & 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)(GET_IVERSION(inode) & 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 08c45b09bb78..cde772057445 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 d25ec32d3b31..5e0a196ae42b 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 6d76162f398c..0616c638dd3f 100644 --- a/fs/sdfat/misc.c +++ b/fs/sdfat/misc.c @@ -52,6 +52,58 @@ #define ST_LOG(fmt, ...) #endif +/************************************************************************* + * 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 + + +#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 @@ -99,6 +151,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); @@ -174,9 +227,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_compat *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 */ @@ -191,22 +245,45 @@ void sdfat_time_fat2unix(struct sdfat_sb_info *sbi, struct timespec_compat *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_sec += sys_tz.tz_minuteswest * SECS_PER_MIN; - 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; + } + + /* 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_compat *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) { @@ -262,7 +339,7 @@ void sdfat_time_unix2fat(struct sdfat_sb_info *sbi, struct timespec_compat *ts, TIMESTAMP_T *tm_now(struct sdfat_sb_info *sbi, TIMESTAMP_T *tp) { - struct timespec_compat ts = CURRENT_TIME_SEC; + sdfat_timespec_t ts = CURRENT_TIME_SEC; DATE_TIME_T dt; sdfat_time_unix2fat(sbi, &ts, &dt); @@ -273,6 +350,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 c8d99082ac49..2c9bb64386f9 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) */ @@ -889,6 +903,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) { @@ -1215,7 +1238,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 != GET_IVERSION(dentry->d_parent->d_inode)))) { + (dentry->d_time != + (unsigned long)inode_peek_iversion(dentry->d_parent->d_inode)))) { ret = 0; } spin_unlock(&dentry->d_lock); @@ -2363,7 +2387,7 @@ static int __sdfat_create(struct inode *dir, struct dentry *dentry) { struct super_block *sb = dir->i_sb; struct inode *inode; - struct timespec_compat ts; + sdfat_timespec_t ts; FILE_ID_T fid; loff_t i_pos; int err; @@ -2380,7 +2404,7 @@ static int __sdfat_create(struct inode *dir, struct dentry *dentry) __lock_d_revalidate(dentry); - INC_IVERSION(dir); + inode_inc_iversion(dir); dir->i_ctime = dir->i_mtime = dir->i_atime = ts; if (IS_DIRSYNC(dir)) (void) sdfat_sync_inode(dir); @@ -2393,7 +2417,8 @@ static int __sdfat_create(struct inode *dir, struct dentry *dentry) err = PTR_ERR(inode); goto out; } - INC_IVERSION(inode); + + inode_inc_iversion(inode); inode->i_mtime = inode->i_atime = inode->i_ctime = ts; /* timestamp is already written, so mark_inode_dirty() is unneeded. */ @@ -2510,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 = GET_IVERSION(dir); + dentry->d_time = (unsigned long)inode_peek_iversion(dir); __unlock_super(sb); dentry = d_splice_alias(inode, dentry); @@ -2528,7 +2553,8 @@ 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_compat ts; + + sdfat_timespec_t ts; int err; __lock_super(sb); @@ -2547,7 +2573,7 @@ static int sdfat_unlink(struct inode *dir, struct dentry *dentry) __lock_d_revalidate(dentry); - INC_IVERSION(dir); + inode_inc_iversion(dir); dir->i_mtime = dir->i_atime = ts; if (IS_DIRSYNC(dir)) (void) sdfat_sync_inode(dir); @@ -2557,7 +2583,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 = GET_IVERSION(dir); + dentry->d_time = (unsigned long)inode_peek_iversion(dir); out: __unlock_d_revalidate(dentry); __unlock_super(sb); @@ -2569,7 +2595,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_compat ts; + sdfat_timespec_t ts; FILE_ID_T fid; loff_t i_pos; int err; @@ -2599,7 +2625,7 @@ static int sdfat_symlink(struct inode *dir, struct dentry *dentry, const char *t __lock_d_revalidate(dentry); - INC_IVERSION(dir); + inode_inc_iversion(dir); dir->i_ctime = dir->i_mtime = dir->i_atime = ts; if (IS_DIRSYNC(dir)) (void) sdfat_sync_inode(dir); @@ -2612,7 +2638,7 @@ static int sdfat_symlink(struct inode *dir, struct dentry *dentry, const char *t err = PTR_ERR(inode); goto out; } - INC_IVERSION(inode); + inode_inc_iversion(inode); inode->i_mtime = inode->i_atime = inode->i_ctime = ts; /* timestamp is already written, so mark_inode_dirty() is unneeded. */ @@ -2636,7 +2662,7 @@ static int __sdfat_mkdir(struct inode *dir, struct dentry *dentry) { struct super_block *sb = dir->i_sb; struct inode *inode; - struct timespec_compat ts; + sdfat_timespec_t ts; FILE_ID_T fid; loff_t i_pos; int err; @@ -2653,7 +2679,7 @@ static int __sdfat_mkdir(struct inode *dir, struct dentry *dentry) __lock_d_revalidate(dentry); - INC_IVERSION(dir); + inode_inc_iversion(dir); dir->i_ctime = dir->i_mtime = dir->i_atime = ts; if (IS_DIRSYNC(dir)) (void) sdfat_sync_inode(dir); @@ -2667,7 +2693,8 @@ static int __sdfat_mkdir(struct inode *dir, struct dentry *dentry) err = PTR_ERR(inode); goto out; } - INC_IVERSION(inode); + + inode_inc_iversion(inode); inode->i_mtime = inode->i_atime = inode->i_ctime = ts; /* timestamp is already written, so mark_inode_dirty() is unneeded. */ @@ -2687,7 +2714,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_compat ts; + sdfat_timespec_t ts; int err; __lock_super(sb); @@ -2704,7 +2731,7 @@ static int sdfat_rmdir(struct inode *dir, struct dentry *dentry) __lock_d_revalidate(dentry); - INC_IVERSION(dir); + inode_inc_iversion(dir); dir->i_mtime = dir->i_atime = ts; if (IS_DIRSYNC(dir)) (void) sdfat_sync_inode(dir); @@ -2715,7 +2742,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 = GET_IVERSION(dir); + dentry->d_time = (unsigned long)inode_peek_iversion(dir); out: __unlock_d_revalidate(dentry); __unlock_super(sb); @@ -2728,7 +2755,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_compat ts; + sdfat_timespec_t ts; loff_t i_pos; int err; @@ -2752,7 +2779,7 @@ static int __sdfat_rename(struct inode *old_dir, struct dentry *old_dentry, __lock_d_revalidate(old_dentry); __lock_d_revalidate(new_dentry); - INC_IVERSION(new_dir); + 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); @@ -2773,7 +2800,7 @@ static int __sdfat_rename(struct inode *old_dir, struct dentry *old_dentry, inc_nlink(new_dir); } - INC_IVERSION(old_dir); + 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); @@ -2882,6 +2909,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) { @@ -2897,7 +2936,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; } @@ -2911,7 +2950,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))) || @@ -2919,7 +2958,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; } /* @@ -2935,6 +2975,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 */ @@ -2944,8 +2988,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; } @@ -3894,7 +3937,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; - INC_IVERSION(inode); + inode_inc_iversion(inode); inode->i_generation = get_seconds(); if (fsapi_read_inode(inode, &info) < 0) { @@ -3972,7 +4015,7 @@ static struct inode *sdfat_build_inode(struct super_block *sb, goto out; } inode->i_ino = iunique(sb, SDFAT_ROOT_INO); - SET_IVERSION(inode, 1); + inode_set_iversion(inode, 1); err = sdfat_fill_inode(inode, fid); if (err) { iput(inode); @@ -4801,7 +4844,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_compat ts; + sdfat_timespec_t ts; FS_INFO_T *fsi = &(sbi->fsi); DIR_ENTRY_T info; @@ -4827,7 +4870,7 @@ static int sdfat_read_root(struct inode *inode) inode->i_uid = sbi->options.fs_uid; inode->i_gid = sbi->options.fs_gid; - INC_IVERSION(inode); + 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; @@ -4955,7 +4998,7 @@ static int sdfat_fill_super(struct super_block *sb, void *data, int silent) } root_inode->i_ino = SDFAT_ROOT_INO; - SET_IVERSION(root_inode, 1); + inode_set_iversion(root_inode, 1); err = sdfat_read_root(root_inode); if (err) { @@ -5127,7 +5170,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; } @@ -5142,6 +5185,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"); @@ -5172,6 +5219,7 @@ static int __init init_sdfat_fs(void) return 0; error: + sdfat_uevent_uninit(); sdfat_statistics_uninit(); if (sdfat_kset) { @@ -5189,6 +5237,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 1fda91ff42a9..e62198146a8a 100644 --- a/fs/sdfat/sdfat.h +++ b/fs/sdfat/sdfat.h @@ -241,6 +241,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 */ @@ -382,6 +388,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; @@ -397,9 +415,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_compat *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_compat *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 487ac3f8af3a..b012e406fb23 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 3cd0b73691ac..44e44e03d847 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"