Merge pull request #1 from namjaejeon/exfat-next

Exfat next
This commit is contained in:
Namjae Jeon 2020-02-08 19:14:21 +09:00 committed by GitHub
commit 3772ef846a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 506 additions and 8 deletions

View File

@ -71,7 +71,43 @@ script:
- ./configure > /dev/null
- make -j$((`nproc`+1)) > /dev/null
- sudo ./check generic/001
- sudo ./check generic/002
- sudo ./check generic/005
- sudo ./check generic/006
- sudo ./check generic/007
- sudo ./check generic/011
- sudo ./check generic/014
- sudo ./check generic/028
- sudo ./check generic/029
- sudo ./check generic/030
- sudo ./check generic/035
- sudo ./check generic/036
- sudo ./check generic/069
- sudo ./check generic/071
- sudo ./check generic/074
- sudo ./check generic/075
- sudo ./check generic/080
- sudo ./check generic/084
- sudo ./check generic/086
- sudo ./check generic/095
- sudo ./check generic/098
- sudo ./check generic/112
- sudo ./check generic/113
- sudo ./check generic/114
- sudo ./check generic/120
- sudo ./check generic/123
- sudo ./check generic/124
- sudo ./check generic/127
- sudo ./check generic/131
- sudo ./check generic/132
- sudo ./check generic/135
- sudo ./check generic/141
- sudo ./check generic/169
- sudo ./check generic/198
- sudo ./check generic/207
- sudo ./check generic/208
- sudo ./check generic/209
- sudo ./check generic/210
- sudo ./check generic/212
- sudo ./check generic/214
- sudo ./check generic/215
- sudo ./check generic/221
- sudo ./check generic/228

View File

@ -6,7 +6,7 @@ ifneq ($(KERNELRELEASE),)
obj-$(CONFIG_EXFAT_FS) += exfat.o
exfat-y := inode.o namei.o dir.o super.o fatent.o cache.o nls.o misc.o \
file.o balloc.io
file.o balloc.o
else
# Called from external kernel module build

11
dir.c
View File

@ -3,6 +3,7 @@
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*/
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/bio.h>
#include <linux/buffer_head.h>
@ -443,7 +444,11 @@ int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir,
{
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
struct timespec64 ts = current_time(inode);
#else
struct timespec64 ts = CURRENT_TIME_SEC;
#endif
sector_t sector;
struct exfat_dentry *ep;
struct buffer_head *bh;
@ -884,7 +889,13 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
num_entries = type == ES_ALL_ENTRIES ?
ep->dentry.file.num_ext + 1 : type;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
es = kmalloc(struct_size(es, entries, num_entries), GFP_KERNEL);
#else
es = kmalloc((offsetof(struct exfat_entry_set_cache, entries) +
(num_entries) * sizeof(struct exfat_dentry)), GFP_KERNEL);
#endif
if (!es)
goto release_bh;

View File

@ -6,6 +6,7 @@
#ifndef _EXFAT_FS_H
#define _EXFAT_FS_H
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/ratelimit.h>
#include <linux/nls.h>
@ -422,8 +423,14 @@ extern const struct file_operations exfat_file_operations;
int __exfat_truncate(struct inode *inode, loff_t new_size);
void exfat_truncate(struct inode *inode, loff_t size);
int exfat_setattr(struct dentry *dentry, struct iattr *attr);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
int exfat_getattr(const struct path *path, struct kstat *stat,
unsigned int request_mask, unsigned int query_flags);
#else
int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
#endif
/* namei.c */
extern const struct dentry_operations exfat_dentry_ops;

View File

@ -26,7 +26,11 @@ static int exfat_mirror_bh(struct super_block *sb, sector_t sec,
memcpy(c_bh->b_data, bh->b_data, sb->s_blocksize);
set_buffer_uptodate(c_bh);
mark_buffer_dirty(c_bh);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
if (sb->s_flags & SB_SYNCHRONOUS)
#else
if (sb->s_flags & MS_SYNCHRONOUS)
#endif
err = sync_dirty_buffer(c_bh);
brelse(c_bh);
}
@ -75,7 +79,11 @@ int exfat_ent_set(struct super_block *sb, unsigned int loc,
fat_entry = (__le32 *)&(bh->b_data[off]);
*fat_entry = cpu_to_le32(content);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
exfat_update_bh(sb, bh, sb->s_flags & SB_SYNCHRONOUS);
#else
exfat_update_bh(sb, bh, sb->s_flags & MS_SYNCHRONOUS);
#endif
exfat_mirror_bh(sb, sec, bh);
brelse(bh);
return 0;

37
file.c
View File

@ -3,9 +3,14 @@
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*/
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/cred.h>
#include <linux/buffer_head.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/cred.h>
#else
#include <linux/compat.h>
#endif
#include "exfat_raw.h"
#include "exfat_fs.h"
@ -20,7 +25,11 @@ static int exfat_cont_expand(struct inode *inode, loff_t size)
if (err)
return err;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
inode->i_ctime = inode->i_mtime = current_time(inode);
#else
inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
#endif
mark_inode_dirty(inode);
if (!IS_SYNC(inode))
@ -160,7 +169,11 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
return -EIO;
ep2 = ep + 1;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
ts = current_time(inode);
#else
ts = CURRENT_TIME_SEC;
#endif
exfat_set_entry_time(sbi, &ts,
&ep->dentry.file.modify_tz,
&ep->dentry.file.modify_time,
@ -243,7 +256,11 @@ void exfat_truncate(struct inode *inode, loff_t size)
if (err)
goto write_size;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
inode->i_ctime = inode->i_mtime = current_time(inode);
#else
inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
#endif
if (IS_DIRSYNC(inode))
exfat_sync_inode(inode);
else
@ -266,16 +283,28 @@ write_size:
mutex_unlock(&sbi->s_lock);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
int exfat_getattr(const struct path *path, struct kstat *stat,
unsigned int request_mask, unsigned int query_flags)
#else
int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
#endif
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
struct inode *inode = d_backing_inode(path->dentry);
struct exfat_inode_info *ei = EXFAT_I(inode);
#else
struct inode *inode = d_inode(dentry);
#endif
generic_fillattr(inode, stat);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
stat->result_mask |= STATX_BTIME;
stat->btime.tv_sec = ei->i_crtime.tv_sec;
stat->btime.tv_nsec = ei->i_crtime.tv_nsec;
#endif
stat->blksize = EXFAT_SB(inode->i_sb)->cluster_size;
return 0;
}
@ -303,7 +332,13 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
ATTR_TIMES_SET);
}
#if ((LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)) && \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 37))) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0))
error = setattr_prepare(dentry, attr);
#else
error = inode_change_ok(inode, attr);
#endif
attr->ia_valid = ia_valid;
if (error)
goto out;

26
inode.c
View File

@ -3,6 +3,7 @@
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*/
#include <linux/version.h>
#include <linux/init.h>
#include <linux/buffer_head.h>
#include <linux/mpage.h>
@ -12,8 +13,9 @@
#include <linux/writeback.h>
#include <linux/uio.h>
#include <linux/random.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
#include <linux/iversion.h>
#endif
#include "exfat_raw.h"
#include "exfat_fs.h"
@ -437,7 +439,11 @@ static int exfat_write_end(struct file *file, struct address_space *mapping,
exfat_write_failed(mapping, pos+len);
if (!(err < 0) && !(ei->attr & ATTR_ARCHIVE)) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
inode->i_mtime = inode->i_ctime = current_time(inode);
#else
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
#endif
ei->attr |= ATTR_ARCHIVE;
mark_inode_dirty(inode);
}
@ -445,7 +451,13 @@ static int exfat_write_end(struct file *file, struct address_space *mapping,
return err;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
#else
static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
loff_t offset)
#endif
{
struct address_space *mapping = iocb->ki_filp->f_mapping;
struct inode *inode = mapping->host;
@ -471,7 +483,11 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
* Need to use the DIO_LOCKING for avoiding the race
* condition of exfat_get_block() and ->truncate().
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block);
#else
ret = blockdev_direct_IO(iocb, inode, iter, offset, exfat_get_block);
#endif
if (ret < 0 && (rw & WRITE))
exfat_write_failed(mapping, size);
return ret;
@ -582,7 +598,11 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
inode->i_uid = sbi->options.fs_uid;
inode->i_gid = sbi->options.fs_gid;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
inode_inc_iversion(inode);
#else
inode->i_version++;
#endif
inode->i_generation = prandom_u32();
if (info->attr & ATTR_SUBDIR) { /* directory */
@ -640,7 +660,11 @@ struct inode *exfat_build_inode(struct super_block *sb,
goto out;
}
inode->i_ino = iunique(sb, EXFAT_ROOT_INO);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
inode_set_iversion(inode, 1);
#else
inode->i_version = 1;
#endif
err = exfat_fill_inode(inode, info);
if (err) {
iput(inode);

10
misc.c
View File

@ -39,8 +39,14 @@ void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
if (opts->errors == EXFAT_ERRORS_PANIC) {
panic("exFAT-fs (%s): fs panic from previous error\n",
sb->s_id);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
} else if (opts->errors == EXFAT_ERRORS_RO && !sb_rdonly(sb)) {
sb->s_flags |= SB_RDONLY;
#else
} else if (opts->errors == EXFAT_ERRORS_RO &&
!(sb->s_flags & MS_RDONLY)) {
sb->s_flags |= MS_RDONLY;
#endif
exfat_msg(sb, KERN_ERR, "Filesystem has been set read-only");
}
}
@ -105,7 +111,11 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
struct tm tm;
u16 t, d;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
time64_to_tm(ts->tv_sec, 0, &tm);
#else
time_to_tm(ts->tv_sec, 0, &tm);
#endif
t = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1);
d = ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday;

136
namei.c
View File

@ -3,7 +3,10 @@
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*/
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
#include <linux/iversion.h>
#endif
#include <linux/namei.h>
#include <linux/slab.h>
#include <linux/buffer_head.h>
@ -59,8 +62,13 @@ static int exfat_d_revalidate(struct dentry *dentry, unsigned int flags)
return 0;
spin_lock(&dentry->d_lock);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
ret = inode_eq_iversion(d_inode(dentry->d_parent),
exfat_d_version(dentry));
#else
if (dentry->d_parent->d_inode->i_version != exfat_d_version(dentry))
ret = 0;
#endif
spin_unlock(&dentry->d_lock);
return ret;
}
@ -84,7 +92,11 @@ static int exfat_d_hash(const struct dentry *dentry, struct qstr *qstr)
struct nls_table *t = EXFAT_SB(sb)->nls_io;
const unsigned char *name = qstr->name;
unsigned int len = exfat_striptail_len(qstr->len, qstr->name);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
unsigned long hash = init_name_hash(dentry);
#else
unsigned long hash = init_name_hash();
#endif
int i, charlen;
wchar_t c;
@ -99,8 +111,13 @@ static int exfat_d_hash(const struct dentry *dentry, struct qstr *qstr)
return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
static int exfat_d_cmp(const struct dentry *dentry, unsigned int len,
const char *str, const struct qstr *name)
#else
static int exfat_d_cmp(const struct dentry *parent, const struct dentry *dentry,
unsigned int len, const char *str, const struct qstr *name)
#endif
{
struct super_block *sb = dentry->d_sb;
struct nls_table *t = EXFAT_SB(sb)->nls_io;
@ -137,7 +154,11 @@ static int exfat_utf8_d_hash(const struct dentry *dentry, struct qstr *qstr)
struct super_block *sb = dentry->d_sb;
const unsigned char *name = qstr->name;
unsigned int len = exfat_striptail_len(qstr->len, qstr->name);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
unsigned long hash = init_name_hash(dentry);
#else
unsigned long hash = init_name_hash();
#endif
int i, charlen;
unicode_t u;
@ -163,8 +184,14 @@ static int exfat_utf8_d_hash(const struct dentry *dentry, struct qstr *qstr)
return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
static int exfat_utf8_d_cmp(const struct dentry *dentry, unsigned int len,
const char *str, const struct qstr *name)
#else
static int exfat_utf8_d_cmp(const struct dentry *parent,
const struct dentry *dentry, unsigned int len,
const char *str, const struct qstr *name)
#endif
{
struct super_block *sb = dentry->d_sb;
unsigned int alen = exfat_striptail_len(name->len, name->name);
@ -580,8 +607,16 @@ static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
if (err)
goto unlock;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
inode_inc_iversion(dir);
#else
dir->i_version++;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
dir->i_ctime = dir->i_mtime = current_time(dir);
#else
dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
#endif
if (IS_DIRSYNC(dir))
exfat_sync_inode(dir);
else
@ -592,9 +627,19 @@ static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
if (IS_ERR(inode))
goto unlock;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
inode_inc_iversion(inode);
#else
inode->i_version++;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
inode->i_mtime = inode->i_atime = inode->i_ctime =
EXFAT_I(inode)->i_crtime = current_time(inode);
#else
inode->i_mtime = inode->i_atime = inode->i_ctime =
EXFAT_I(inode)->i_crtime = CURRENT_TIME_SEC;
#endif
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
d_instantiate(dentry, inode);
@ -629,10 +674,18 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
return num_entries;
/* check the validation of hint_stat and initialize it if required */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
if (ei->version != (inode_peek_iversion_raw(dir) & 0xffffffff)) {
#else
if (ei->version != (dir->i_version & 0xffffffff)) {
#endif
ei->hint_stat.clu = cdir.dir;
ei->hint_stat.eidx = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
ei->version = (inode_peek_iversion_raw(dir) & 0xffffffff);
#else
ei->version = (dir->i_version & 0xffffffff);
#endif
ei->hint_femp.eidx = EXFAT_HINT_NONE;
}
@ -798,7 +851,11 @@ static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry,
out:
mutex_unlock(&EXFAT_SB(sb)->s_lock);
if (!inode)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
exfat_d_version_set(dentry, inode_query_iversion(dir));
#else
exfat_d_version_set(dentry, dir->i_version);
#endif
return d_splice_alias(inode, dentry);
unlock:
@ -852,17 +909,33 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
ei->dir.dir = DIR_DELETED;
exfat_set_vol_flags(sb, VOL_CLEAN);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
inode_inc_iversion(dir);
#else
dir->i_version++;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
dir->i_mtime = dir->i_atime = current_time(dir);
#else
dir->i_mtime = dir->i_atime = CURRENT_TIME_SEC;
#endif
if (IS_DIRSYNC(dir))
exfat_sync_inode(dir);
else
mark_inode_dirty(dir);
clear_nlink(inode);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
inode->i_mtime = inode->i_atime = current_time(inode);
#else
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
#endif
exfat_unhash_inode(inode);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
exfat_d_version_set(dentry, inode_query_iversion(dir));
#else
exfat_d_version_set(dentry, dir->i_version);
#endif
unlock:
mutex_unlock(&EXFAT_SB(sb)->s_lock);
return err;
@ -885,8 +958,17 @@ static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
if (err)
goto unlock;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
inode_inc_iversion(dir);
#else
dir->i_version++;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
dir->i_ctime = dir->i_mtime = current_time(dir);
#else
dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
#endif
if (IS_DIRSYNC(dir))
exfat_sync_inode(dir);
else
@ -900,9 +982,18 @@ static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
goto unlock;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
inode_inc_iversion(inode);
#else
inode->i_version++;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
inode->i_mtime = inode->i_atime = inode->i_ctime =
EXFAT_I(inode)->i_crtime = current_time(inode);
#else
inode->i_mtime = inode->i_atime = inode->i_ctime =
EXFAT_I(inode)->i_crtime = CURRENT_TIME_SEC;
#endif
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
d_instantiate(dentry, inode);
@ -1017,8 +1108,16 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
ei->dir.dir = DIR_DELETED;
exfat_set_vol_flags(sb, VOL_CLEAN);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
inode_inc_iversion(dir);
#else
dir->i_version++;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
dir->i_mtime = dir->i_atime = current_time(dir);
#else
dir->i_mtime = dir->i_atime = CURRENT_TIME_SEC;
#endif
if (IS_DIRSYNC(dir))
exfat_sync_inode(dir);
else
@ -1026,9 +1125,17 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
drop_nlink(dir);
clear_nlink(inode);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
inode->i_mtime = inode->i_atime = current_time(inode);
#else
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
#endif
exfat_unhash_inode(inode);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
exfat_d_version_set(dentry, inode_query_iversion(dir));
#else
exfat_d_version_set(dentry, dir->i_version);
#endif
unlock:
mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock);
return err;
@ -1359,15 +1466,21 @@ out:
return ret;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags)
#else
static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
#endif
{
struct inode *old_inode, *new_inode;
struct super_block *sb = old_dir->i_sb;
loff_t i_pos;
int err;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
/*
* The VFS already checks for existence, so for local filesystems
* the RENAME_NOREPLACE implementation is equivalent to plain rename.
@ -1375,6 +1488,7 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
*/
if (flags & ~RENAME_NOREPLACE)
return -EINVAL;
#endif
mutex_lock(&EXFAT_SB(sb)->s_lock);
old_inode = old_dentry->d_inode;
@ -1384,9 +1498,18 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
if (err)
goto unlock;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
inode_inc_iversion(new_dir);
#else
new_dir->i_version++;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
EXFAT_I(new_dir)->i_crtime = current_time(new_dir);
#else
new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
EXFAT_I(new_dir)->i_crtime = CURRENT_TIME_SEC;
#endif
if (IS_DIRSYNC(new_dir))
exfat_sync_inode(new_dir);
else
@ -1407,8 +1530,16 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
inc_nlink(new_dir);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
inode_inc_iversion(old_dir);
#else
old_dir->i_version++;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir);
#else
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
#endif
if (IS_DIRSYNC(old_dir))
exfat_sync_inode(old_dir);
else
@ -1427,8 +1558,13 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
"abnormal access to an inode dropped");
WARN_ON(new_inode->i_nlink == 0);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
new_inode->i_ctime = EXFAT_I(new_inode)->i_crtime =
current_time(new_inode);
#else
new_inode->i_ctime = EXFAT_I(new_inode)->i_crtime =
CURRENT_TIME_SEC;
#endif
}
unlock:

237
super.c
View File

@ -3,8 +3,14 @@
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*/
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#else
#include <linux/cred.h>
#include <linux/parser.h>
#endif
#include <linux/module.h>
#include <linux/init.h>
#include <linux/time.h>
@ -14,13 +20,20 @@
#include <linux/seq_file.h>
#include <linux/blkdev.h>
#include <linux/fs_struct.h>
#include <linux/iversion.h>
#include <linux/nls.h>
#include <linux/buffer_head.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
#include <linux/iversion.h>
#endif
#include "exfat_raw.h"
#include "exfat_fs.h"
#ifndef CONFIG_EXFAT_DEFAULT_IOCHARSET /* if Kconfig lacked iocharset */
#define CONFIG_EXFAT_DEFAULT_IOCHARSET "utf8"
#endif
static char exfat_default_iocharset[] = CONFIG_EXFAT_DEFAULT_IOCHARSET;
static struct kmem_cache *exfat_inode_cachep;
@ -112,7 +125,11 @@ int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
/* skip updating volume dirty flag,
* if this volume has been mounted with read-only
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
if (sb_rdonly(sb))
#else
if (sb->s_flags & MS_RDONLY)
#endif
return 0;
if (!sbi->pbr_bh) {
@ -185,14 +202,31 @@ static struct inode *exfat_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
static void exfat_free_inode(struct inode *inode)
{
kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode));
}
#else
static void exfat_i_callback(struct rcu_head *head)
{
struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode));
}
static void exfat_destroy_inode(struct inode *inode)
{
call_rcu(&inode->i_rcu, exfat_i_callback);
}
#endif
static const struct super_operations exfat_sops = {
.alloc_inode = exfat_alloc_inode,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
.free_inode = exfat_free_inode,
#else
.destroy_inode = exfat_destroy_inode,
#endif
.write_inode = exfat_write_inode,
.evict_inode = exfat_evict_inode,
.put_super = exfat_put_super,
@ -201,6 +235,7 @@ static const struct super_operations exfat_sops = {
.show_options = exfat_show_options,
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
enum {
Opt_uid,
Opt_gid,
@ -299,6 +334,137 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
return 0;
}
#else
enum {
Opt_uid,
Opt_gid,
Opt_umask,
Opt_dmask,
Opt_fmask,
Opt_allow_utime,
Opt_charset,
Opt_err_cont,
Opt_err_panic,
Opt_err_ro,
Opt_err,
Opt_discard,
Opt_fs,
};
static const match_table_t exfat_tokens = {
{Opt_uid, "uid=%u"},
{Opt_gid, "gid=%u"},
{Opt_umask, "umask=%o"},
{Opt_dmask, "dmask=%o"},
{Opt_fmask, "fmask=%o"},
{Opt_allow_utime, "allow_utime=%o"},
{Opt_charset, "iocharset=%s"},
{Opt_err_cont, "errors=continue"},
{Opt_err_panic, "errors=panic"},
{Opt_err_ro, "errors=remount-ro"},
{Opt_discard, "discard"},
{Opt_err, NULL}
};
static int parse_options(struct super_block *sb, char *options, int silent,
struct exfat_mount_options *opts)
{
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
char *tmpstr;
opts->fs_uid = current_uid();
opts->fs_gid = current_gid();
opts->fs_fmask = opts->fs_dmask = current->fs->umask;
opts->allow_utime = -1;
opts->iocharset = exfat_default_iocharset;
opts->utf8 = 0;
opts->errors = EXFAT_ERRORS_RO;
opts->discard = 0;
if (!options)
goto out;
while ((p = strsep(&options, ",")) != NULL) {
int token;
if (!*p)
continue;
token = match_token(p, exfat_tokens, args);
switch (token) {
case Opt_uid:
if (match_int(&args[0], &option))
return 0;
opts->fs_uid = make_kuid(current_user_ns(), option);
break;
case Opt_gid:
if (match_int(&args[0], &option))
return 0;
opts->fs_gid = make_kgid(current_user_ns(), option);
break;
case Opt_umask:
case Opt_dmask:
case Opt_fmask:
if (match_octal(&args[0], &option))
return 0;
if (token != Opt_dmask)
opts->fs_fmask = option;
if (token != Opt_fmask)
opts->fs_dmask = option;
break;
case Opt_allow_utime:
if (match_octal(&args[0], &option))
return 0;
opts->allow_utime = option & (0022);
break;
case Opt_charset:
if (opts->iocharset != exfat_default_iocharset)
kfree(opts->iocharset);
tmpstr = match_strdup(&args[0]);
if (!tmpstr)
return -ENOMEM;
opts->iocharset = tmpstr;
break;
case Opt_err_cont:
opts->errors = EXFAT_ERRORS_CONT;
break;
case Opt_err_panic:
opts->errors = EXFAT_ERRORS_PANIC;
break;
case Opt_err_ro:
opts->errors = EXFAT_ERRORS_RO;
break;
case Opt_discard:
opts->discard = 1;
break;
default:
if (!silent) {
exfat_msg(sb, KERN_ERR,
"unrecognized mount option \"%s\" or missing value",
p);
}
return -EINVAL;
}
}
out:
if (opts->allow_utime == -1)
opts->allow_utime = ~opts->fs_dmask & (0022);
if (opts->discard) {
struct request_queue *q = bdev_get_queue(sb->s_bdev);
if (!blk_queue_discard(q))
exfat_msg(sb, KERN_WARNING,
"mounting with \"discard\" option, but the device does not support discard");
opts->discard = 0;
}
return 0;
}
#endif
static void exfat_hash_init(struct super_block *sb)
{
@ -342,7 +508,12 @@ static int exfat_read_root(struct inode *inode)
inode->i_uid = sbi->options.fs_uid;
inode->i_gid = sbi->options.fs_gid;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
inode_inc_iversion(inode);
#else
inode->i_version++;
#endif
inode->i_generation = 0;
inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, 0777);
inode->i_op = &exfat_dir_inode_operations;
@ -355,8 +526,13 @@ static int exfat_read_root(struct inode *inode)
EXFAT_I(inode)->i_size_ondisk = i_size_read(inode);
exfat_save_attr(inode, ATTR_SUBDIR);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime =
current_time(inode);
#else
inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime =
CURRENT_TIME_SEC;
#endif
exfat_cache_init_inode(inode);
return 0;
}
@ -524,12 +700,17 @@ free_bh:
return ret;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
#else
static int exfat_fill_super(struct super_block *sb, void *data, int silent)
#endif
{
struct exfat_sb_info *sbi = sb->s_fs_info;
struct exfat_mount_options *opts = &sbi->options;
struct inode *root_inode;
int err;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
struct exfat_sb_info *sbi = sb->s_fs_info;
struct exfat_mount_options *opts = &sbi->options;
if (opts->allow_utime == (unsigned short)-1)
opts->allow_utime = ~opts->fs_dmask & 0022;
@ -542,14 +723,43 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
"mounting with \"discard\" option, but the device does not support discard");
opts->discard = 0;
}
#else
struct exfat_sb_info *sbi;
/*
* GFP_KERNEL is ok here, because while we do hold the
* supeblock lock, memory pressure can't call back into
* the filesystem, since we're only just about to mount
* it and have no inodes etc active!
*/
sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL);
if (!sbi)
return -ENOMEM;
mutex_init(&sbi->s_lock);
sb->s_fs_info = sbi;
ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
err = parse_options(sb, data, silent, &sbi->options);
if (err) {
exfat_msg(sb, KERN_ERR, "failed to parse options");
goto check_nls_io;
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
sb->s_flags |= SB_NODIRATIME;
#else
sb->s_flags |= MS_NODIRATIME;
#endif
sb->s_magic = EXFAT_SUPER_MAGIC;
sb->s_op = &exfat_sops;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
sb->s_time_gran = 1;
sb->s_time_min = EXFAT_MIN_TIMESTAMP_SECS;
sb->s_time_max = EXFAT_MAX_TIMESTAMP_SECS;
#endif
err = __exfat_fill_super(sb);
if (err) {
@ -561,7 +771,11 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
exfat_hash_init(sb);
if (!strcmp(sbi->options.iocharset, "utf8"))
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
opts->utf8 = 1;
#else
sbi->options.utf8 = 1;
#endif
else {
sbi->nls_io = load_nls(sbi->options.iocharset);
if (!sbi->nls_io) {
@ -585,7 +799,12 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
}
root_inode->i_ino = EXFAT_ROOT_INO;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
inode_set_iversion(root_inode, 1);
#else
root_inode->i_version = 1;
#endif
err = exfat_read_root(root_inode);
if (err) {
exfat_msg(sb, KERN_ERR, "failed to initialize root inode.");
@ -620,6 +839,7 @@ check_nls_io:
return err;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
static int exfat_get_tree(struct fs_context *fc)
{
return get_tree_bdev(fc, exfat_fill_super);
@ -660,12 +880,23 @@ static int exfat_init_fs_context(struct fs_context *fc)
fc->ops = &exfat_context_ops;
return 0;
}
#else
static struct dentry *exfat_fs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return mount_bdev(fs_type, flags, dev_name, data, exfat_fill_super);
}
#endif
static struct file_system_type exfat_fs_type = {
.owner = THIS_MODULE,
.name = "exfat",
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
.init_fs_context = exfat_init_fs_context,
.parameters = &exfat_parameters,
#else
.mount = exfat_fs_mount,
#endif
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};