Merge tag '5.19.1' of https://github.com/namjaejeon/linux-exfat-oot into android13-5.4-lahaina

exfat: release 5.19.1 version
Major changes are:
 - fix referencing wrong parent directory information during rename.
 - introduce a sys_tz mount option to use system timezone.
 - improve performance while zeroing a cluster with dirsync mount option.
 - fix slab-out-bounds in exat_clear_bitmap() reported from syzbot.
 - Add keep_last_dots mount option to allow access to paths with trailing dots.
 - Avoid repetitive volume dirty bit set/clear to improve storage life time.
 - Fix ->i_blocks truncation issue that still exists elsewhere.
 - 4 cleanups & typos fixes.
 - Move super block magic number to magic.h
 - Fix missing REQ_SYNC in exfat_update_bhs().
 - Fix ->i_blocks truncation issue caused by wrong 32bit mask.

 Conflicts:
	fs/exfat/Kconfig
	fs/exfat/Makefile
	fs/exfat/balloc.c
	fs/exfat/cache.c
	fs/exfat/dir.c
	fs/exfat/exfat_fs.h
	fs/exfat/exfat_raw.h
	fs/exfat/fatent.c
	fs/exfat/file.c
	fs/exfat/inode.c
	fs/exfat/misc.c
	fs/exfat/namei.c
	fs/exfat/nls.c
	fs/exfat/super.c

Change-Id: I5fd39768b9fb0419602fc77153de39291e6a1f1d
This commit is contained in:
Michael Bestas 2022-10-02 20:39:21 +03:00
commit 566743e4e4
No known key found for this signature in database
GPG Key ID: CC95044519BE6669
17 changed files with 1382 additions and 263 deletions

65
fs/exfat/.travis_cmd_wrapper.pl Executable file
View File

@ -0,0 +1,65 @@
#!/usr/bin/perl
#
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (C) 2019 Samsung Electronics Co., Ltd.
#
use strict;
sub tweak_sysctl()
{
`sudo sysctl kernel.hardlockup_panic=0`;
`sudo sysctl kernel.hung_task_panic=0`;
`sudo sysctl kernel.panic=128`;
`sudo sysctl kernel.panic_on_io_nmi=0`;
`sudo sysctl kernel.panic_on_oops=0`;
`sudo sysctl kernel.panic_on_rcu_stall=0`;
`sudo sysctl kernel.panic_on_unrecovered_nmi=0`;
`sudo sysctl kernel.panic_on_warn=0`;
`sudo sysctl kernel.softlockup_panic=0`;
`sudo sysctl kernel.unknown_nmi_panic=0`;
}
sub execute($$)
{
my $cmd = shift;
my $timeout = shift;
my $output = "Timeout";
my $status = 1;
$timeout = 8 * 60 if (!defined $timeout);
tweak_sysctl();
eval {
local $SIG{ALRM} = sub {
print "TIMEOUT:\n";
system("top -n 1"), print "top\n";
system("free"), print "free\n";
system("dmesg"), print "dmesg\n";
die "Timeout\n";
};
print "Executing $cmd with timeout $timeout\n";
alarm $timeout;
$output = `$cmd`;
$status = $?;
alarm 0;
print $output."\n";
print "Finished: status $status\n";
};
if ($@) {
die unless $@ eq "Timeout\n";
}
}
if (! defined $ARGV[0]) {
print "Usage:\n\t./.travis_cmd_wrapper.pl command [timeout seconds]\n";
exit 1;
}
execute($ARGV[0], $ARGV[1]);

View File

@ -0,0 +1,37 @@
#!/bin/sh
#
# A simple script we are using to get the latest mainline kernel
# tar ball
#
wget https://www.kernel.org/releases.json
if [ $? -ne 0 ]; then
echo "Could not download kernel.org/releases.json"
exit 1
fi
VER=$(cat releases.json | python2.7 -c "import sys, json; print json.load(sys.stdin)['latest_stable']['version']")
if [ $? -ne 0 ]; then
echo "Could not parse release.json"
exit 1
fi
if [ "z$VER" = "z" ]; then
echo "Could not determine latest release version"
exit 1
fi
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-"$VER".tar.gz
if [ $? -ne 0 ]; then
echo "Could not download $VER kernel version"
exit 1
fi
tar xf linux-"$VER".tar.gz
if [ $? -ne 0 ]; then
echo "Could not untar kernel tar ball"
exit 1
fi
mv linux-"$VER" linux

View File

@ -16,7 +16,6 @@ config EXFAT_DEFAULT_IOCHARSET
depends on EXFAT_FS
help
Set this to the default input/output character set to use for
converting between the encoding that is used for user visible
filenames and the UTF-16 character encoding that the exFAT
filesystem uses. This can be overridden with the "iocharset" mount
option for the exFAT filesystems.
converting between the encoding is used for user visible filename and
UTF-16 character that exfat filesystem use, and can be overridden with
the "iocharset" mount option for exFAT filesystems.

View File

@ -2,7 +2,39 @@
#
# Makefile for the linux exFAT filesystem support.
#
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.o
else
# Called from external kernel module build
KERNELRELEASE ?= $(shell uname -r)
KDIR ?= /lib/modules/${KERNELRELEASE}/build
MDIR ?= /lib/modules/${KERNELRELEASE}
PWD := $(shell pwd)
export CONFIG_EXFAT_FS := m
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
help:
$(MAKE) -C $(KDIR) M=$(PWD) help
install: exfat.ko
rm -f ${MDIR}/kernel/fs/exfat/exfat.ko
install -m644 -b -D exfat.ko ${MDIR}/kernel/fs/exfat/exfat.ko
depmod -aq
uninstall:
rm -rf ${MDIR}/kernel/fs/exfat
depmod -aq
endif
.PHONY : all clean install uninstall

53
fs/exfat/README.md Normal file
View File

@ -0,0 +1,53 @@
## exFAT filesystem
This is the exfat filesystem for support from the linux 4.1 kernel
to the latest kernel.
## Installing as a stand-alone module
Install prerequisite package for Fedora, RHEL:
```
yum install kernel-devel-$(uname -r)
```
Build step:
```
make
sudo make install
```
To load the driver manually, run this as root:
```
modprobe exfat
```
## Installing as a part of the kernel
1. Let's take [linux] as the path to your kernel source dir.
```
cd [linux]
cp -ar exfat [linux]/fs/
```
2. edit [linux]/fs/Kconfig
```
source "fs/fat/Kconfig"
+source "fs/exfat/Kconfig"
source "fs/ntfs/Kconfig"
```
3. edit [linux]/fs/Makefile
```
obj-$(CONFIG_FAT_FS) += fat/
+obj-$(CONFIG_EXFAT_FS) += exfat/
obj-$(CONFIG_BFS_FS) += bfs/
```
4. make menuconfig and set exfat
```
File systems --->
DOS/FAT/NT Filesystems --->
<M> exFAT filesystem support
(utf8) Default iocharset for exFAT
```
build your kernel

View File

@ -3,9 +3,15 @@
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*/
#include <linux/version.h>
#include <linux/blkdev.h>
#include <linux/slab.h>
#include <linux/buffer_head.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/signal.h>
#else
#include <linux/sched.h>
#endif
#include "exfat_raw.h"
#include "exfat_fs.h"
@ -105,7 +111,7 @@ int exfat_load_bitmap(struct super_block *sb)
struct exfat_dentry *ep;
struct buffer_head *bh;
ep = exfat_get_dentry(sb, &clu, i, &bh, NULL);
ep = exfat_get_dentry(sb, &clu, i, &bh);
if (!ep)
return -EIO;
@ -141,32 +147,26 @@ void exfat_free_bitmap(struct exfat_sb_info *sbi)
kfree(sbi->vol_amap);
}
/*
* If the value of "clu" is 0, it means cluster 2 which is the first cluster of
* the cluster heap.
*/
int exfat_set_bitmap(struct inode *inode, unsigned int clu)
int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync)
{
int i, b;
unsigned int ent_idx;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
WARN_ON(clu < EXFAT_FIRST_CLUSTER);
if (!is_valid_cluster(sbi, clu))
return -EINVAL;
ent_idx = CLUSTER_TO_BITMAP_ENT(clu);
i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx);
set_bit_le(b, sbi->vol_amap[i]->b_data);
exfat_update_bh(sbi->vol_amap[i], IS_DIRSYNC(inode));
exfat_update_bh(sbi->vol_amap[i], sync);
return 0;
}
/*
* If the value of "clu" is 0, it means cluster 2 which is the first cluster of
* the cluster heap.
*/
void exfat_clear_bitmap(struct inode *inode, unsigned int clu)
void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
{
int i, b;
unsigned int ent_idx;
@ -174,20 +174,21 @@ void exfat_clear_bitmap(struct inode *inode, unsigned int clu)
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_mount_options *opts = &sbi->options;
WARN_ON(clu < EXFAT_FIRST_CLUSTER);
if (!is_valid_cluster(sbi, clu))
return;
ent_idx = CLUSTER_TO_BITMAP_ENT(clu);
i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx);
clear_bit_le(b, sbi->vol_amap[i]->b_data);
exfat_update_bh(sbi->vol_amap[i], IS_DIRSYNC(inode));
exfat_update_bh(sbi->vol_amap[i], sync);
if (opts->discard) {
int ret_discard;
ret_discard = sb_issue_discard(sb,
exfat_cluster_to_sector(sbi, clu +
EXFAT_RESERVED_CLUSTERS),
exfat_cluster_to_sector(sbi, clu),
(1 << sbi->sect_per_clus_bits), GFP_NOFS, 0);
if (ret_discard == -EOPNOTSUPP) {
@ -272,4 +273,84 @@ int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count)
*ret_count = count;
return 0;
}
}
int exfat_trim_fs(struct inode *inode, struct fstrim_range *range)
{
unsigned int trim_begin, trim_end, count, next_free_clu;
u64 clu_start, clu_end, trim_minlen, trimmed_total = 0;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
int err = 0;
clu_start = max_t(u64, range->start >> sbi->cluster_size_bits,
EXFAT_FIRST_CLUSTER);
clu_end = clu_start + (range->len >> sbi->cluster_size_bits) - 1;
trim_minlen = range->minlen >> sbi->cluster_size_bits;
if (clu_start >= sbi->num_clusters || range->len < sbi->cluster_size)
return -EINVAL;
if (clu_end >= sbi->num_clusters)
clu_end = sbi->num_clusters - 1;
mutex_lock(&sbi->bitmap_lock);
trim_begin = trim_end = exfat_find_free_bitmap(sb, clu_start);
if (trim_begin == EXFAT_EOF_CLUSTER)
goto unlock;
next_free_clu = exfat_find_free_bitmap(sb, trim_end + 1);
if (next_free_clu == EXFAT_EOF_CLUSTER)
goto unlock;
do {
if (next_free_clu == trim_end + 1) {
/* extend trim range for continuous free cluster */
trim_end++;
} else {
/* trim current range if it's larger than trim_minlen */
count = trim_end - trim_begin + 1;
if (count >= trim_minlen) {
err = sb_issue_discard(sb,
exfat_cluster_to_sector(sbi, trim_begin),
count * sbi->sect_per_clus, GFP_NOFS, 0);
if (err)
goto unlock;
trimmed_total += count;
}
/* set next start point of the free hole */
trim_begin = trim_end = next_free_clu;
}
if (next_free_clu >= clu_end)
break;
if (fatal_signal_pending(current)) {
err = -ERESTARTSYS;
goto unlock;
}
next_free_clu = exfat_find_free_bitmap(sb, next_free_clu + 1);
} while (next_free_clu != EXFAT_EOF_CLUSTER &&
next_free_clu > trim_end);
/* try to trim remainder */
count = trim_end - trim_begin + 1;
if (count >= trim_minlen) {
err = sb_issue_discard(sb, exfat_cluster_to_sector(sbi, trim_begin),
count * sbi->sect_per_clus, GFP_NOFS, 0);
if (err)
goto unlock;
trimmed_total += count;
}
unlock:
mutex_unlock(&sbi->bitmap_lock);
range->len = trimmed_total << sbi->cluster_size_bits;
return err;
}

View File

@ -311,4 +311,4 @@ int exfat_get_cluster(struct inode *inode, unsigned int cluster,
exfat_cache_add(inode, &cid);
return 0;
}
}

View File

@ -3,7 +3,9 @@
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*/
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/bio.h>
#include <linux/buffer_head.h>
@ -62,8 +64,7 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_entry *dir_entry)
{
int i, dentries_per_clu, dentries_per_clu_bits = 0, num_ext;
unsigned int type, clu_offset;
sector_t sector;
unsigned int type, clu_offset, max_dentries;
struct exfat_chain dir, clu;
struct exfat_uni_name uni_name;
struct exfat_dentry *ep;
@ -85,6 +86,8 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
dentries_per_clu = sbi->dentries_per_clu;
dentries_per_clu_bits = ilog2(dentries_per_clu);
max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES,
(u64)sbi->num_clusters << dentries_per_clu_bits);
clu_offset = dentry >> dentries_per_clu_bits;
exfat_chain_dup(&clu, &dir);
@ -108,11 +111,11 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
}
}
while (clu.dir != EXFAT_EOF_CLUSTER) {
while (clu.dir != EXFAT_EOF_CLUSTER && dentry < max_dentries) {
i = dentry & (dentries_per_clu - 1);
for ( ; i < dentries_per_clu; i++, dentry++) {
ep = exfat_get_dentry(sb, &clu, i, &bh, &sector);
ep = exfat_get_dentry(sb, &clu, i, &bh);
if (!ep)
return -EIO;
@ -146,14 +149,14 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
0);
*uni_name.name = 0x0;
exfat_get_uniname_from_ext_entry(sb, &dir, dentry,
exfat_get_uniname_from_ext_entry(sb, &clu, i,
uni_name.name);
exfat_utf16_to_nls(sb, &uni_name,
dir_entry->namebuf.lfn,
dir_entry->namebuf.lfnbuf_len);
brelse(bh);
ep = exfat_get_dentry(sb, &clu, i + 1, &bh, NULL);
ep = exfat_get_dentry(sb, &clu, i + 1, &bh);
if (!ep)
return -EIO;
dir_entry->size =
@ -244,7 +247,7 @@ static int exfat_iterate(struct file *filp, struct dir_context *ctx)
if (err)
goto unlock;
get_new:
if (cpos >= i_size_read(inode))
if (ei->flags == ALLOC_NO_FAT_CHAIN && cpos >= i_size_read(inode))
goto end_of_dir;
err = exfat_readdir(inode, &cpos, &de);
@ -306,6 +309,10 @@ const struct file_operations exfat_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.iterate = exfat_iterate,
.unlocked_ioctl = exfat_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = exfat_compat_ioctl,
#endif
.fsync = exfat_file_fsync,
};
@ -315,7 +322,7 @@ int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu)
exfat_chain_set(clu, EXFAT_EOF_CLUSTER, 0, ALLOC_NO_FAT_CHAIN);
ret = exfat_alloc_cluster(inode, 1, clu);
ret = exfat_alloc_cluster(inode, 1, clu, IS_DIRSYNC(inode));
if (ret)
return ret;
@ -437,16 +444,25 @@ 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);
struct timespec64 ts = current_time(inode);
sector_t sector;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
struct timespec64 ts;
#else
struct timespec ts;
#endif
struct exfat_dentry *ep;
struct buffer_head *bh;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
ts = current_time(inode);
#else
ts = CURRENT_TIME_SEC;
#endif
/*
* We cannot use exfat_get_dentry_set here because file ep is not
* initialized yet.
*/
ep = exfat_get_dentry(sb, p_dir, entry, &bh, &sector);
ep = exfat_get_dentry(sb, p_dir, entry, &bh);
if (!ep)
return -EIO;
@ -470,7 +486,7 @@ int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir,
exfat_update_bh(bh, IS_DIRSYNC(inode));
brelse(bh);
ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh, &sector);
ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh);
if (!ep)
return -EIO;
@ -489,12 +505,11 @@ int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
struct super_block *sb = inode->i_sb;
int ret = 0;
int i, num_entries;
sector_t sector;
u16 chksum;
struct exfat_dentry *ep, *fep;
struct buffer_head *fbh, *bh;
fep = exfat_get_dentry(sb, p_dir, entry, &fbh, &sector);
fep = exfat_get_dentry(sb, p_dir, entry, &fbh);
if (!fep)
return -EIO;
@ -502,7 +517,7 @@ int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
chksum = exfat_calc_chksum16(fep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
for (i = 1; i < num_entries; i++) {
ep = exfat_get_dentry(sb, p_dir, entry + i, &bh, NULL);
ep = exfat_get_dentry(sb, p_dir, entry + i, &bh);
if (!ep) {
ret = -EIO;
goto release_fbh;
@ -524,13 +539,12 @@ int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
{
struct super_block *sb = inode->i_sb;
int i;
sector_t sector;
unsigned short *uniname = p_uniname->name;
struct exfat_dentry *ep;
struct buffer_head *bh;
int sync = IS_DIRSYNC(inode);
ep = exfat_get_dentry(sb, p_dir, entry, &bh, &sector);
ep = exfat_get_dentry(sb, p_dir, entry, &bh);
if (!ep)
return -EIO;
@ -538,7 +552,7 @@ int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
exfat_update_bh(bh, sync);
brelse(bh);
ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh, &sector);
ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh);
if (!ep)
return -EIO;
@ -548,7 +562,7 @@ int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
brelse(bh);
for (i = EXFAT_FIRST_CLUSTER; i < num_entries; i++) {
ep = exfat_get_dentry(sb, p_dir, entry + i, &bh, &sector);
ep = exfat_get_dentry(sb, p_dir, entry + i, &bh);
if (!ep)
return -EIO;
@ -567,12 +581,11 @@ int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
{
struct super_block *sb = inode->i_sb;
int i;
sector_t sector;
struct exfat_dentry *ep;
struct buffer_head *bh;
for (i = order; i < num_entries; i++) {
ep = exfat_get_dentry(sb, p_dir, entry + i, &bh, &sector);
ep = exfat_get_dentry(sb, p_dir, entry + i, &bh);
if (!ep)
return -EIO;
@ -649,8 +662,8 @@ static int exfat_walk_fat_chain(struct super_block *sb,
return 0;
}
int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir,
int entry, sector_t *sector, int *offset)
static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir,
int entry, sector_t *sector, int *offset)
{
int ret;
unsigned int off, clu = 0;
@ -710,8 +723,7 @@ static int exfat_dir_readahead(struct super_block *sb, sector_t sec)
}
struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
struct exfat_chain *p_dir, int entry, struct buffer_head **bh,
sector_t *sector)
struct exfat_chain *p_dir, int entry, struct buffer_head **bh)
{
unsigned int dentries_per_page = EXFAT_B_TO_DEN(PAGE_SIZE);
int off;
@ -733,8 +745,6 @@ struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
if (!*bh)
return NULL;
if (sector)
*sector = sec;
return (struct exfat_dentry *)((*bh)->b_data + off);
}
@ -885,7 +895,7 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
es->bh[es->num_bh++] = bh;
}
/* validiate cached dentries */
/* validate cached dentries */
for (i = 1; i < num_entries; i++) {
ep = exfat_get_dentry_cached(es, i);
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
@ -906,14 +916,19 @@ enum {
};
/*
* return values:
* >= 0 : return dir entiry position with the name in dir
* -ENOENT : entry with the name does not exist
* -EIO : I/O error
* @ei: inode info of parent directory
* @p_dir: directory structure of parent directory
* @num_entries:entry size of p_uniname
* @hint_opt: If p_uniname is found, filled with optimized dir/entry
* for traversing cluster chain.
* @return:
* >= 0: file directory entry position where the name exists
* -ENOENT: entry with the name does not exist
* -EIO: I/O error
*/
int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
int num_entries, unsigned int type)
int num_entries, unsigned int type, struct exfat_hint *hint_opt)
{
int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len;
int order, step, name_len = 0;
@ -948,7 +963,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
if (rewind && dentry == end_eidx)
goto not_found;
ep = exfat_get_dentry(sb, &clu, i, &bh, NULL);
ep = exfat_get_dentry(sb, &clu, i, &bh);
if (!ep)
return -EIO;
@ -990,6 +1005,8 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
if (entry_type == TYPE_FILE || entry_type == TYPE_DIR) {
step = DIRENT_STEP_FILE;
hint_opt->clu = clu.dir;
hint_opt->eidx = i;
if (type == TYPE_ALL || type == entry_type) {
num_ext = ep->dentry.file.num_ext;
step = DIRENT_STEP_STRM;
@ -1131,7 +1148,7 @@ int exfat_count_ext_entries(struct super_block *sb, struct exfat_chain *p_dir,
struct buffer_head *bh;
for (i = 0, entry++; i < ep->dentry.file.num_ext; i++, entry++) {
ext_ep = exfat_get_dentry(sb, p_dir, entry, &bh, NULL);
ext_ep = exfat_get_dentry(sb, p_dir, entry, &bh);
if (!ext_ep)
return -EIO;
@ -1161,7 +1178,7 @@ int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir)
while (clu.dir != EXFAT_EOF_CLUSTER) {
for (i = 0; i < dentries_per_clu; i++) {
ep = exfat_get_dentry(sb, &clu, i, &bh, NULL);
ep = exfat_get_dentry(sb, &clu, i, &bh);
if (!ep)
return -EIO;
entry_type = exfat_get_entry_type(ep);
@ -1186,4 +1203,4 @@ int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir)
}
return count;
}
}

View File

@ -6,11 +6,19 @@
#ifndef _EXFAT_FS_H
#define _EXFAT_FS_H
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/ratelimit.h>
#include <linux/nls.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
#include <linux/magic.h>
#else
#define EXFAT_SUPER_MAGIC 0x2011BAB0UL
#endif
#define EXFAT_VERSION "5.19.1"
#define EXFAT_ROOT_INO 1
#define EXFAT_CLUSTERS_UNTRACKED (~0u)
@ -183,9 +191,15 @@ struct exfat_dir_entry {
unsigned short attr;
loff_t size;
unsigned int num_subdirs;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
struct timespec64 atime;
struct timespec64 mtime;
struct timespec64 crtime;
#else
struct timespec atime;
struct timespec mtime;
struct timespec crtime;
#endif
struct exfat_dentry_namebuf namebuf;
};
@ -204,7 +218,9 @@ struct exfat_mount_options {
/* on error: continue, panic, remount-ro */
enum exfat_error_mode errors;
unsigned utf8:1, /* Use of UTF-8 character set */
discard:1; /* Issue discard requests on deletions */
sys_tz:1, /* Use local timezone */
discard:1, /* Issue discard requests on deletions */
keep_last_dots:1; /* Keep trailing periods in paths */
int time_offset; /* Offset of timestamps from UTC (in minutes) */
};
@ -238,6 +254,7 @@ struct exfat_sb_info {
unsigned int used_clusters; /* number of used clusters */
struct mutex s_lock; /* superblock lock */
struct mutex bitmap_lock; /* bitmap lock */
struct exfat_mount_options options;
struct nls_table *nls_io; /* Charset used for input and display */
struct ratelimit_state ratelimit;
@ -294,7 +311,11 @@ struct exfat_inode_info {
struct rw_semaphore truncate_lock;
struct inode vfs_inode;
/* File creation time */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
struct timespec64 i_crtime;
#else
struct timespec i_crtime;
#endif
};
static inline struct exfat_sb_info *EXFAT_SB(struct super_block *sb)
@ -380,6 +401,12 @@ static inline int exfat_sector_to_cluster(struct exfat_sb_info *sbi,
EXFAT_RESERVED_CLUSTERS;
}
static inline bool is_valid_cluster(struct exfat_sb_info *sbi,
unsigned int clus)
{
return clus >= EXFAT_FIRST_CLUSTER && clus < sbi->num_clusters;
}
/* super.c */
int exfat_set_volume_dirty(struct super_block *sb);
int exfat_clear_volume_dirty(struct super_block *sb);
@ -388,7 +415,7 @@ int exfat_clear_volume_dirty(struct super_block *sb);
#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu)
int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
struct exfat_chain *p_chain);
struct exfat_chain *p_chain, bool sync_bmap);
int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain);
int exfat_ent_get(struct super_block *sb, unsigned int loc,
unsigned int *content);
@ -407,19 +434,38 @@ int exfat_count_num_clusters(struct super_block *sb,
/* balloc.c */
int exfat_load_bitmap(struct super_block *sb);
void exfat_free_bitmap(struct exfat_sb_info *sbi);
int exfat_set_bitmap(struct inode *inode, unsigned int clu);
void exfat_clear_bitmap(struct inode *inode, unsigned int clu);
int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync);
void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync);
unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu);
int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count);
int exfat_trim_fs(struct inode *inode, struct fstrim_range *range);
/* file.c */
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);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
struct iattr *attr);
int exfat_getattr(struct user_namespace *mnt_userns, const struct path *path,
struct kstat *stat, unsigned int request_mask,
unsigned int query_flags);
#else
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
#endif
int exfat_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
long exfat_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg);
/* namei.c */
extern const struct dentry_operations exfat_dentry_ops;
@ -450,13 +496,10 @@ void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es);
int exfat_calc_num_entries(struct exfat_uni_name *p_uniname);
int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
int num_entries, unsigned int type);
int num_entries, unsigned int type, struct exfat_hint *hint_opt);
int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu);
int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir,
int entry, sector_t *sector, int *offset);
struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
struct exfat_chain *p_dir, int entry, struct buffer_head **bh,
sector_t *sector);
struct exfat_chain *p_dir, int entry, struct buffer_head **bh);
struct exfat_dentry *exfat_get_dentry_cached(struct exfat_entry_set_cache *es,
int num);
struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
@ -506,11 +549,19 @@ void exfat_msg(struct super_block *sb, const char *lv, const char *fmt, ...)
#define exfat_info(sb, fmt, ...) \
exfat_msg(sb, KERN_INFO, fmt, ##__VA_ARGS__)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
u8 tz, __le16 time, __le16 date, u8 time_cs);
void exfat_truncate_atime(struct timespec64 *ts);
void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
u8 *tz, __le16 *time, __le16 *date, u8 *time_cs);
#else
void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec *ts,
u8 tz, __le16 time, __le16 date, u8 time_cs);
void exfat_truncate_atime(struct timespec *ts);
void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec *ts,
u8 *tz, __le16 *time, __le16 *date, u8 *time_cs);
#endif
u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type);
u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type);
void exfat_update_bh(struct buffer_head *bh, int sync);
@ -519,4 +570,4 @@ void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
unsigned int size, unsigned char flags);
void exfat_chain_dup(struct exfat_chain *dup, struct exfat_chain *ec);
#endif /* !_EXFAT_FS_H */
#endif /* !_EXFAT_FS_H */

View File

@ -77,6 +77,10 @@
#define EXFAT_FILE_NAME_LEN 15
#define EXFAT_MIN_SECT_SIZE_BITS 9
#define EXFAT_MAX_SECT_SIZE_BITS 12
#define EXFAT_MAX_SECT_PER_CLUS_BITS(x) (25 - (x)->sect_size_bits)
/* EXFAT: Main and Backup Boot Sector (512 bytes) */
struct boot_sector {
__u8 jmp_boot[BOOTSEC_JUMP_BOOT_LEN];
@ -161,4 +165,4 @@ struct exfat_dentry {
/* Dec 31 GMT 23:59:59 2107 */
#define EXFAT_MAX_TIMESTAMP_SECS 4354819199LL
#endif /* !_EXFAT_RAW_H */
#endif /* !_EXFAT_RAW_H */

View File

@ -6,6 +6,7 @@
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <linux/buffer_head.h>
#include <linux/blkdev.h>
#include "exfat_raw.h"
#include "exfat_fs.h"
@ -26,7 +27,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,20 +80,16 @@ 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(bh, sb->s_flags & SB_SYNCHRONOUS);
#else
exfat_update_bh(bh, sb->s_flags & MS_SYNCHRONOUS);
#endif
exfat_mirror_bh(sb, sec, bh);
brelse(bh);
return 0;
}
static inline bool is_valid_cluster(struct exfat_sb_info *sbi,
unsigned int clus)
{
if (clus < EXFAT_FIRST_CLUSTER || sbi->num_clusters <= clus)
return false;
return true;
}
int exfat_ent_get(struct super_block *sb, unsigned int loc,
unsigned int *content)
{
@ -151,12 +152,14 @@ int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain,
return 0;
}
int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
/* This function must be called with bitmap_lock held */
static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
{
unsigned int num_clusters = 0;
unsigned int clu;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
int cur_cmap_i, next_cmap_i;
unsigned int num_clusters = 0;
unsigned int clu;
/* invalid cluster number */
if (p_chain->dir == EXFAT_FREE_CLUSTER ||
@ -176,21 +179,51 @@ int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
clu = p_chain->dir;
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
do {
exfat_clear_bitmap(inode, clu);
clu++;
cur_cmap_i = next_cmap_i =
BITMAP_OFFSET_SECTOR_INDEX(sb, CLUSTER_TO_BITMAP_ENT(clu));
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
unsigned int last_cluster = p_chain->dir + p_chain->size - 1;
do {
bool sync = false;
if (clu < last_cluster)
next_cmap_i =
BITMAP_OFFSET_SECTOR_INDEX(sb, CLUSTER_TO_BITMAP_ENT(clu+1));
/* flush bitmap only if index would be changed or for last cluster */
if (clu == last_cluster || cur_cmap_i != next_cmap_i) {
sync = true;
cur_cmap_i = next_cmap_i;
}
exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode)));
clu++;
num_clusters++;
} while (num_clusters < p_chain->size);
} else {
do {
exfat_clear_bitmap(inode, clu);
bool sync = false;
unsigned int n_clu = clu;
int err = exfat_get_next_cluster(sb, &n_clu);
if (exfat_get_next_cluster(sb, &clu))
goto dec_used_clus;
if (err || n_clu == EXFAT_EOF_CLUSTER)
sync = true;
else
next_cmap_i =
BITMAP_OFFSET_SECTOR_INDEX(sb, CLUSTER_TO_BITMAP_ENT(n_clu));
if (cur_cmap_i != next_cmap_i) {
sync = true;
cur_cmap_i = next_cmap_i;
}
exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode)));
clu = n_clu;
num_clusters++;
if (err)
goto dec_used_clus;
} while (clu != EXFAT_EOF_CLUSTER);
}
@ -199,6 +232,17 @@ int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
return 0;
}
int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
{
int ret = 0;
mutex_lock(&EXFAT_SB(inode->i_sb)->bitmap_lock);
ret = __exfat_free_cluster(inode, p_chain);
mutex_unlock(&EXFAT_SB(inode->i_sb)->bitmap_lock);
return ret;
}
int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain,
unsigned int *ret_clu)
{
@ -233,10 +277,9 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu)
{
struct super_block *sb = dir->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct buffer_head *bhs[MAX_BUF_PER_PAGE];
int nr_bhs = MAX_BUF_PER_PAGE;
struct buffer_head *bh;
sector_t blknr, last_blknr;
int err, i, n;
int i;
blknr = exfat_cluster_to_sector(sbi, clu);
last_blknr = blknr + sbi->sect_per_clus;
@ -250,34 +293,34 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu)
}
/* Zeroing the unused blocks on this cluster */
while (blknr < last_blknr) {
for (n = 0; n < nr_bhs && blknr < last_blknr; n++, blknr++) {
bhs[n] = sb_getblk(sb, blknr);
if (!bhs[n]) {
err = -ENOMEM;
goto release_bhs;
}
memset(bhs[n]->b_data, 0, sb->s_blocksize);
}
for (i = blknr; i < last_blknr; i++) {
bh = sb_getblk(sb, i);
if (!bh)
return -ENOMEM;
err = exfat_update_bhs(bhs, n, IS_DIRSYNC(dir));
if (err)
goto release_bhs;
for (i = 0; i < n; i++)
brelse(bhs[i]);
memset(bh->b_data, 0, sb->s_blocksize);
set_buffer_uptodate(bh);
mark_buffer_dirty(bh);
brelse(bh);
}
return 0;
release_bhs:
exfat_err(sb, "failed zeroed sect %llu\n", (unsigned long long)blknr);
for (i = 0; i < n; i++)
bforget(bhs[i]);
return err;
if (IS_DIRSYNC(dir))
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
return sync_blockdev_range(sb->s_bdev,
EXFAT_BLK_TO_B(blknr, sb),
EXFAT_BLK_TO_B(last_blknr, sb) - 1);
#else
return filemap_write_and_wait_range(sb->s_bdev->bd_inode->i_mapping,
EXFAT_BLK_TO_B(blknr, sb),
EXFAT_BLK_TO_B(last_blknr, sb) - 1);
#endif
return 0;
}
int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
struct exfat_chain *p_chain)
struct exfat_chain *p_chain, bool sync_bmap)
{
int ret = -ENOSPC;
unsigned int num_clusters = 0, total_cnt;
@ -297,6 +340,8 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
if (num_alloc > total_cnt - sbi->used_clusters)
return -ENOSPC;
mutex_lock(&sbi->bitmap_lock);
hint_clu = p_chain->dir;
/* find new cluster */
if (hint_clu == EXFAT_EOF_CLUSTER) {
@ -307,8 +352,10 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
}
hint_clu = exfat_find_free_bitmap(sb, sbi->clu_srch_ptr);
if (hint_clu == EXFAT_EOF_CLUSTER)
return -ENOSPC;
if (hint_clu == EXFAT_EOF_CLUSTER) {
ret = -ENOSPC;
goto unlock;
}
}
/* check cluster validation */
@ -318,8 +365,10 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
hint_clu = EXFAT_FIRST_CLUSTER;
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
if (exfat_chain_cont_cluster(sb, p_chain->dir,
num_clusters))
return -EIO;
num_clusters)) {
ret = -EIO;
goto unlock;
}
p_chain->flags = ALLOC_FAT_CHAIN;
}
}
@ -339,7 +388,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
}
/* update allocation bitmap */
if (exfat_set_bitmap(inode, new_clu)) {
if (exfat_set_bitmap(inode, new_clu, sync_bmap)) {
ret = -EIO;
goto free_cluster;
}
@ -369,6 +418,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
sbi->used_clusters += num_clusters;
p_chain->size += num_clusters;
mutex_unlock(&sbi->bitmap_lock);
return 0;
}
@ -388,7 +438,9 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
}
free_cluster:
if (num_clusters)
exfat_free_cluster(inode, p_chain);
__exfat_free_cluster(inode, p_chain);
unlock:
mutex_unlock(&sbi->bitmap_lock);
return ret;
}
@ -421,4 +473,4 @@ int exfat_count_num_clusters(struct super_block *sb,
*ret_count = count;
return 0;
}
}

View File

@ -3,9 +3,13 @@
* 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>
#endif
#include <linux/compat.h>
#include <linux/blkdev.h>
#include "exfat_raw.h"
@ -21,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))
@ -109,8 +117,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
exfat_set_volume_dirty(sb);
num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi);
num_clusters_phys =
EXFAT_B_TO_CLU_ROUND_UP(EXFAT_I(inode)->i_size_ondisk, sbi);
num_clusters_phys = EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags);
@ -151,7 +158,11 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
/* update the directory entry */
if (!evict) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
struct timespec64 ts;
#else
struct timespec ts;
#endif
struct exfat_dentry *ep, *ep2;
struct exfat_entry_set_cache *es;
int err;
@ -163,7 +174,11 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
ep = exfat_get_dentry_cached(es, 0);
ep2 = exfat_get_dentry_cached(es, 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,
@ -218,8 +233,6 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
if (exfat_free_cluster(inode, &clu))
return -EIO;
exfat_clear_volume_dirty(sb);
return 0;
}
@ -227,12 +240,17 @@ void exfat_truncate(struct inode *inode, loff_t size)
{
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 72)
unsigned int blocksize = i_blocksize(inode);
#else
unsigned int blocksize = 1 << inode->i_blkbits;
#endif
loff_t aligned_size;
int err;
mutex_lock(&sbi->s_lock);
if (EXFAT_I(inode)->start_clu == 0) {
if (ei->start_clu == 0) {
/*
* Empty start_clu != ~0 (not allocated)
*/
@ -244,14 +262,18 @@ 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
mark_inode_dirty(inode);
inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) &
~(sbi->cluster_size - 1)) >> inode->i_blkbits;
~((loff_t)sbi->cluster_size - 1)) >> inode->i_blkbits;
write_size:
aligned_size = i_size_read(inode);
if (aligned_size & (blocksize - 1)) {
@ -259,30 +281,56 @@ void exfat_truncate(struct inode *inode, loff_t size)
aligned_size++;
}
if (EXFAT_I(inode)->i_size_ondisk > i_size_read(inode))
EXFAT_I(inode)->i_size_ondisk = aligned_size;
if (ei->i_size_ondisk > i_size_read(inode))
ei->i_size_ondisk = aligned_size;
if (EXFAT_I(inode)->i_size_aligned > i_size_read(inode))
EXFAT_I(inode)->i_size_aligned = aligned_size;
if (ei->i_size_aligned > i_size_read(inode))
ei->i_size_aligned = aligned_size;
mutex_unlock(&sbi->s_lock);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
int exfat_getattr(struct user_namespace *mnt_uerns, const struct path *path,
struct kstat *stat, unsigned int request_mask,
unsigned int query_flags)
#else
#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
#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
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
generic_fillattr(&init_user_ns, inode, stat);
#else
generic_fillattr(inode, stat);
#endif
exfat_truncate_atime(&stat->atime);
#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;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
struct iattr *attr)
#else
int exfat_setattr(struct dentry *dentry, struct iattr *attr)
#endif
{
struct exfat_sb_info *sbi = EXFAT_SB(dentry->d_sb);
struct inode *inode = dentry->d_inode;
@ -305,7 +353,17 @@ 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))
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
error = setattr_prepare(&init_user_ns, dentry, attr);
#else
error = setattr_prepare(dentry, attr);
#endif
#else
error = inode_change_ok(inode, attr);
#endif
attr->ia_valid = ia_valid;
if (error)
goto out;
@ -340,7 +398,11 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
up_write(&EXFAT_I(inode)->truncate_lock);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
setattr_copy(&init_user_ns, inode, attr);
#else
setattr_copy(inode, attr);
#endif
exfat_truncate_atime(&inode->i_atime);
mark_inode_dirty(inode);
@ -348,6 +410,54 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
return error;
}
static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
{
struct request_queue *q = bdev_get_queue(inode->i_sb->s_bdev);
struct fstrim_range range;
int ret = 0;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (!blk_queue_discard(q))
return -EOPNOTSUPP;
if (copy_from_user(&range, (struct fstrim_range __user *)arg, sizeof(range)))
return -EFAULT;
range.minlen = max_t(unsigned int, range.minlen,
q->limits.discard_granularity);
ret = exfat_trim_fs(inode, &range);
if (ret < 0)
return ret;
if (copy_to_user((struct fstrim_range __user *)arg, &range, sizeof(range)))
return -EFAULT;
return 0;
}
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file_inode(filp);
switch (cmd) {
case FITRIM:
return exfat_ioctl_fitrim(inode, arg);
default:
return -ENOTTY;
}
}
#ifdef CONFIG_COMPAT
long exfat_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
return exfat_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
#endif
int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
{
struct inode *inode = filp->f_mapping->host;
@ -361,13 +471,25 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
if (err)
return err;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
return blkdev_issue_flush(inode->i_sb->s_bdev);
#else
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
return blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL);
#else
return blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
#endif
#endif
}
const struct file_operations exfat_file_operations = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter,
.unlocked_ioctl = exfat_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = exfat_compat_ioctl,
#endif
.mmap = generic_file_mmap,
.fsync = exfat_file_fsync,
.splice_read = generic_file_splice_read,
@ -377,4 +499,4 @@ const struct file_operations exfat_file_operations = {
const struct inode_operations exfat_file_inode_operations = {
.setattr = exfat_setattr,
.getattr = exfat_getattr,
};
};

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"
@ -31,7 +33,7 @@ static int __exfat_write_inode(struct inode *inode, int sync)
return 0;
/*
* If the indode is already unlinked, there is no need for updating it.
* If the inode is already unlinked, there is no need for updating it.
*/
if (ei->dir.dir == DIR_DELETED)
return 0;
@ -114,10 +116,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
unsigned int local_clu_offset = clu_offset;
unsigned int num_to_be_allocated = 0, num_clusters = 0;
if (EXFAT_I(inode)->i_size_ondisk > 0)
if (ei->i_size_ondisk > 0)
num_clusters =
EXFAT_B_TO_CLU_ROUND_UP(EXFAT_I(inode)->i_size_ondisk,
sbi);
EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
if (clu_offset >= num_clusters)
num_to_be_allocated = clu_offset - num_clusters + 1;
@ -179,7 +180,8 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
return -EIO;
}
ret = exfat_alloc_cluster(inode, num_to_be_allocated, &new_clu);
ret = exfat_alloc_cluster(inode, num_to_be_allocated, &new_clu,
inode_needs_sync(inode));
if (ret)
return ret;
@ -362,11 +364,18 @@ static int exfat_readpage(struct file *file, struct page *page)
return mpage_readpage(page, exfat_get_block);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
static void exfat_readahead(struct readahead_control *rac)
{
mpage_readahead(rac, exfat_get_block);
}
#else
static int exfat_readpages(struct file *file, struct address_space *mapping,
struct list_head *pages, unsigned int nr_pages)
{
return mpage_readpages(mapping, pages, nr_pages, exfat_get_block);
}
#endif
static int exfat_writepage(struct page *page, struct writeback_control *wbc)
{
@ -416,10 +425,10 @@ static int exfat_write_end(struct file *file, struct address_space *mapping,
err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata);
if (EXFAT_I(inode)->i_size_aligned < i_size_read(inode)) {
if (ei->i_size_aligned < i_size_read(inode)) {
exfat_fs_error(inode->i_sb,
"invalid size(size(%llu) > aligned(%llu)\n",
i_size_read(inode), EXFAT_I(inode)->i_size_aligned);
i_size_read(inode), ei->i_size_aligned);
return -EIO;
}
@ -427,7 +436,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);
}
@ -435,7 +448,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;
@ -461,7 +480,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;
@ -491,8 +514,20 @@ int exfat_block_truncate_page(struct inode *inode, loff_t from)
}
static const struct address_space_operations exfat_aops = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
.dirty_folio = block_dirty_folio,
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0)
.set_page_dirty = __set_page_dirty_buffers,
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
.invalidate_folio = block_invalidate_folio,
#endif
.readpage = exfat_readpage,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
.readahead = exfat_readahead,
#else
.readpages = exfat_readpages,
#endif
.writepage = exfat_writepage,
.writepages = exfat_writepages,
.write_begin = exfat_write_begin,
@ -571,7 +606,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 */
@ -603,7 +642,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
exfat_save_attr(inode, info->attr);
inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) &
~(sbi->cluster_size - 1)) >> inode->i_blkbits;
~((loff_t)sbi->cluster_size - 1)) >> inode->i_blkbits;
inode->i_mtime = info->mtime;
inode->i_ctime = info->mtime;
ei->i_crtime = info->crtime;
@ -627,7 +666,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);
@ -655,4 +698,4 @@ void exfat_evict_inode(struct inode *inode)
clear_inode(inode);
exfat_cache_inval_inode(inode);
exfat_unhash_inode(inode);
}
}

View File

@ -10,6 +10,7 @@
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/buffer_head.h>
#include <linux/blk_types.h>
#include "exfat_raw.h"
#include "exfat_fs.h"
@ -39,8 +40,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_err(sb, "Filesystem has been set read-only");
}
}
@ -65,7 +72,11 @@ void exfat_msg(struct super_block *sb, const char *level, const char *fmt, ...)
#define SECS_PER_MIN (60)
#define TIMEZONE_SEC(x) ((x) * 15 * SECS_PER_MIN)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
static void exfat_adjust_tz(struct timespec64 *ts, u8 tz_off)
#else
static void exfat_adjust_tz(struct timespec *ts, u8 tz_off)
#endif
{
if (tz_off <= 0x3F)
ts->tv_sec -= TIMEZONE_SEC(tz_off);
@ -73,9 +84,21 @@ static void exfat_adjust_tz(struct timespec64 *ts, u8 tz_off)
ts->tv_sec += TIMEZONE_SEC(0x80 - tz_off);
}
static inline int exfat_tz_offset(struct exfat_sb_info *sbi)
{
if (sbi->options.sys_tz)
return -sys_tz.tz_minuteswest;
return sbi->options.time_offset;
}
/* Convert a EXFAT time/date pair to a UNIX date (seconds since 1 1 70). */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
u8 tz, __le16 time, __le16 date, u8 time_cs)
#else
void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec *ts,
u8 tz, __le16 time, __le16 date, u8 time_cs)
#endif
{
u16 t = le16_to_cpu(time);
u16 d = le16_to_cpu(date);
@ -95,18 +118,39 @@ void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
/* Adjust timezone to UTC0. */
exfat_adjust_tz(ts, tz & ~EXFAT_TZ_VALID);
else
/* Convert from local time to UTC using time_offset. */
ts->tv_sec -= sbi->options.time_offset * SECS_PER_MIN;
ts->tv_sec -= exfat_tz_offset(sbi) * SECS_PER_MIN;
}
/* Convert linear UNIX date to a EXFAT time/date pair. */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
u8 *tz, __le16 *time, __le16 *date, u8 *time_cs)
#else
#undef EXFAT_MAX_TIMESTAMP_SECS
#define EXFAT_MAX_TIMESTAMP_SECS 0xffffffff
void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec *ts,
u8 *tz, __le16 *time, __le16 *date, u8 *time_cs)
#endif
{
struct tm tm;
u16 t, d;
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)
if (ts->tv_sec < EXFAT_MIN_TIMESTAMP_SECS) {
ts->tv_sec = EXFAT_MIN_TIMESTAMP_SECS;
ts->tv_nsec = 0;
}
else if (ts->tv_sec > EXFAT_MAX_TIMESTAMP_SECS) {
ts->tv_sec = EXFAT_MAX_TIMESTAMP_SECS;
ts->tv_nsec = 0;
}
#endif
#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;
@ -125,12 +169,12 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
*tz = EXFAT_TZ_VALID;
}
/*
* The timestamp for access_time has double seconds granularity.
* (There is no 10msIncrement field for access_time unlike create/modify_time)
* atime also has only a 2-second resolution.
*/
/* atime has only a 2-second resolution */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
void exfat_truncate_atime(struct timespec64 *ts)
#else
void exfat_truncate_atime(struct timespec *ts)
#endif
{
ts->tv_sec = round_down(ts->tv_sec, 2);
ts->tv_nsec = 0;
@ -180,12 +224,16 @@ int exfat_update_bhs(struct buffer_head **bhs, int nr_bhs, int sync)
set_buffer_uptodate(bhs[i]);
mark_buffer_dirty(bhs[i]);
if (sync)
write_dirty_buffer(bhs[i], 0);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
write_dirty_buffer(bhs[i], REQ_SYNC);
#else
write_dirty_buffer(bhs[i], WRITE_SYNC);
#endif
}
for (i = 0; i < nr_bhs && sync; i++) {
wait_on_buffer(bhs[i]);
if (!buffer_uptodate(bhs[i]))
if (!err && !buffer_uptodate(bhs[i]))
err = -EIO;
}
return err;
@ -202,4 +250,4 @@ void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
void exfat_chain_dup(struct exfat_chain *dup, struct exfat_chain *ec)
{
return exfat_chain_set(dup, ec->dir, ec->size, ec->flags);
}
}

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>
@ -33,7 +36,7 @@ static inline void exfat_d_version_set(struct dentry *dentry,
*/
static int exfat_d_revalidate(struct dentry *dentry, unsigned int flags)
{
int ret;
int ret = 1;
if (flags & LOOKUP_RCU)
return -ECHILD;
@ -59,17 +62,25 @@ 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;
}
/* returns the length of a struct qstr, ignoring trailing dots */
static unsigned int exfat_striptail_len(unsigned int len, const char *name)
/* returns the length of a struct qstr, ignoring trailing dots if necessary */
static unsigned int exfat_striptail_len(unsigned int len, const char *name,
bool keep_last_dots)
{
while (len && name[len - 1] == '.')
len--;
if (!keep_last_dots) {
while (len && name[len - 1] == '.')
len--;
}
return len;
}
@ -83,8 +94,13 @@ static int exfat_d_hash(const struct dentry *dentry, struct qstr *qstr)
struct super_block *sb = dentry->d_sb;
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);
unsigned int len = exfat_striptail_len(qstr->len, qstr->name,
EXFAT_SB(sb)->options.keep_last_dots);
#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,13 +115,20 @@ 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;
unsigned int alen = exfat_striptail_len(name->len, name->name);
unsigned int blen = exfat_striptail_len(len, str);
unsigned int alen = exfat_striptail_len(name->len, name->name,
EXFAT_SB(sb)->options.keep_last_dots);
unsigned int blen = exfat_striptail_len(len, str,
EXFAT_SB(sb)->options.keep_last_dots);
wchar_t c1, c2;
int charlen, i;
@ -136,8 +159,13 @@ 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);
unsigned int len = exfat_striptail_len(qstr->len, qstr->name,
EXFAT_SB(sb)->options.keep_last_dots);
#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;
@ -157,12 +185,21 @@ 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);
unsigned int blen = exfat_striptail_len(len, str);
unsigned int alen = exfat_striptail_len(name->len, name->name,
EXFAT_SB(sb)->options.keep_last_dots);
unsigned int blen = exfat_striptail_len(len, str,
EXFAT_SB(sb)->options.keep_last_dots);
unicode_t u_a, u_b;
int charlen, i;
@ -229,7 +266,7 @@ static int exfat_search_empty_slot(struct super_block *sb,
i = dentry & (dentries_per_clu - 1);
for (; i < dentries_per_clu; i++, dentry++) {
ep = exfat_get_dentry(sb, &clu, i, &bh, NULL);
ep = exfat_get_dentry(sb, &clu, i, &bh);
if (!ep)
return -EIO;
type = exfat_get_entry_type(ep);
@ -306,7 +343,6 @@ static int exfat_find_empty_entry(struct inode *inode,
{
int dentry;
unsigned int ret, last_clu;
sector_t sector;
loff_t size = 0;
struct exfat_chain clu;
struct exfat_dentry *ep = NULL;
@ -340,7 +376,7 @@ static int exfat_find_empty_entry(struct inode *inode,
exfat_chain_set(&clu, last_clu + 1, 0, p_dir->flags);
/* allocate a cluster */
ret = exfat_alloc_cluster(inode, 1, &clu);
ret = exfat_alloc_cluster(inode, 1, &clu, IS_DIRSYNC(inode));
if (ret)
return ret;
@ -379,7 +415,7 @@ static int exfat_find_empty_entry(struct inode *inode,
struct buffer_head *bh;
ep = exfat_get_dentry(sb,
&(ei->dir), ei->entry + 1, &bh, &sector);
&(ei->dir), ei->entry + 1, &bh);
if (!ep)
return -EIO;
@ -395,9 +431,9 @@ static int exfat_find_empty_entry(struct inode *inode,
/* directory inode should be updated in here */
i_size_write(inode, size);
EXFAT_I(inode)->i_size_ondisk += sbi->cluster_size;
EXFAT_I(inode)->i_size_aligned += sbi->cluster_size;
EXFAT_I(inode)->flags = p_dir->flags;
ei->i_size_ondisk += sbi->cluster_size;
ei->i_size_aligned += sbi->cluster_size;
ei->flags = p_dir->flags;
inode->i_blocks += 1 << sbi->sect_per_clus_bits;
}
@ -417,13 +453,25 @@ static int __exfat_resolve_path(struct inode *inode, const unsigned char *path,
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
int pathlen = strlen(path);
/* strip all trailing periods */
namelen = exfat_striptail_len(strlen(path), path);
/*
* get the length of the pathname excluding
* trailing periods, if any.
*/
namelen = exfat_striptail_len(pathlen, path, false);
if (EXFAT_SB(sb)->options.keep_last_dots) {
/*
* Do not allow the creation of files with names
* ending with period(s).
*/
if (!lookup && (namelen < pathlen))
return -EINVAL;
namelen = pathlen;
}
if (!namelen)
return -ENOENT;
if (strlen(path) > (MAX_NAME_LENGTH * MAX_CHARSET_SIZE))
if (pathlen > (MAX_NAME_LENGTH * MAX_CHARSET_SIZE))
return -ENAMETOOLONG;
/*
@ -541,8 +589,13 @@ static int exfat_add_entry(struct inode *inode, const char *path,
return ret;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
static int exfat_create(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode, bool excl)
#else
static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool excl)
#endif
{
struct super_block *sb = dir->i_sb;
struct inode *inode;
@ -555,12 +608,19 @@ static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
exfat_set_volume_dirty(sb);
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE,
&info);
exfat_clear_volume_dirty(sb);
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
@ -572,9 +632,19 @@ 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(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
exfat_truncate_atime(&inode->i_atime);
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
@ -596,6 +666,8 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
struct exfat_inode_info *ei = EXFAT_I(dir);
struct exfat_dentry *ep, *ep2;
struct exfat_entry_set_cache *es;
/* for optimized dir & entry to prevent long traverse of cluster chain */
struct exfat_hint hint_opt;
if (qname->len == 0)
return -ENOENT;
@ -610,16 +682,24 @@ 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;
}
/* search the file name for directories */
dentry = exfat_find_dir_entry(sb, ei, &cdir, &uni_name,
num_entries, TYPE_ALL);
num_entries, TYPE_ALL, &hint_opt);
if (dentry < 0)
return dentry; /* -error value */
@ -628,6 +708,11 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
info->entry = dentry;
info->num_subdirs = 0;
/* adjust cdir to the optimized value */
cdir.dir = hint_opt.clu;
if (cdir.flags & ALLOC_NO_FAT_CHAIN)
cdir.size -= dentry / sbi->dentries_per_clu;
dentry = hint_opt.eidx;
es = exfat_get_dentry_set(sb, &cdir, dentry, ES_2_ENTRIES);
if (!es)
return -EIO;
@ -755,7 +840,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:
@ -772,7 +861,6 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
struct inode *inode = dentry->d_inode;
struct exfat_inode_info *ei = EXFAT_I(inode);
struct buffer_head *bh;
sector_t sector;
int num_entries, entry, err = 0;
mutex_lock(&EXFAT_SB(sb)->s_lock);
@ -784,7 +872,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
goto unlock;
}
ep = exfat_get_dentry(sb, &cdir, entry, &bh, &sector);
ep = exfat_get_dentry(sb, &cdir, entry, &bh);
if (!ep) {
err = -EIO;
goto unlock;
@ -807,10 +895,17 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
/* This doesn't modify ei */
ei->dir.dir = DIR_DELETED;
exfat_clear_volume_dirty(sb);
#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
exfat_truncate_atime(&dir->i_atime);
if (IS_DIRSYNC(dir))
exfat_sync_inode(dir);
@ -818,16 +913,29 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
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_truncate_atime(&inode->i_atime);
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;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
static int exfat_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode)
#else
static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
#endif
{
struct super_block *sb = dir->i_sb;
struct inode *inode;
@ -840,12 +948,20 @@ static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
exfat_set_volume_dirty(sb);
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_DIR,
&info);
exfat_clear_volume_dirty(sb);
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
@ -858,9 +974,18 @@ 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(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
exfat_truncate_atime(&inode->i_atime);
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
@ -887,7 +1012,7 @@ static int exfat_check_dir_empty(struct super_block *sb,
while (clu.dir != EXFAT_EOF_CLUSTER) {
for (i = 0; i < dentries_per_clu; i++) {
ep = exfat_get_dentry(sb, &clu, i, &bh, NULL);
ep = exfat_get_dentry(sb, &clu, i, &bh);
if (!ep)
return -EIO;
type = exfat_get_entry_type(ep);
@ -924,7 +1049,6 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
struct buffer_head *bh;
sector_t sector;
int num_entries, entry, err;
mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
@ -949,7 +1073,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
goto unlock;
}
ep = exfat_get_dentry(sb, &cdir, entry, &bh, &sector);
ep = exfat_get_dentry(sb, &cdir, entry, &bh);
if (!ep) {
err = -EIO;
goto unlock;
@ -971,10 +1095,17 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
goto unlock;
}
ei->dir.dir = DIR_DELETED;
exfat_clear_volume_dirty(sb);
#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
exfat_truncate_atime(&dir->i_atime);
if (IS_DIRSYNC(dir))
exfat_sync_inode(dir);
@ -983,10 +1114,18 @@ 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_truncate_atime(&inode->i_atime);
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;
@ -997,13 +1136,12 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
struct exfat_inode_info *ei)
{
int ret, num_old_entries, num_new_entries;
sector_t sector_old, sector_new;
struct exfat_dentry *epold, *epnew;
struct super_block *sb = inode->i_sb;
struct buffer_head *new_bh, *old_bh;
int sync = IS_DIRSYNC(inode);
epold = exfat_get_dentry(sb, p_dir, oldentry, &old_bh, &sector_old);
epold = exfat_get_dentry(sb, p_dir, oldentry, &old_bh);
if (!epold)
return -EIO;
@ -1024,8 +1162,7 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
if (newentry < 0)
return newentry; /* -EIO or -ENOSPC */
epnew = exfat_get_dentry(sb, p_dir, newentry, &new_bh,
&sector_new);
epnew = exfat_get_dentry(sb, p_dir, newentry, &new_bh);
if (!epnew)
return -EIO;
@ -1038,12 +1175,10 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
brelse(old_bh);
brelse(new_bh);
epold = exfat_get_dentry(sb, p_dir, oldentry + 1, &old_bh,
&sector_old);
epold = exfat_get_dentry(sb, p_dir, oldentry + 1, &old_bh);
if (!epold)
return -EIO;
epnew = exfat_get_dentry(sb, p_dir, newentry + 1, &new_bh,
&sector_new);
epnew = exfat_get_dentry(sb, p_dir, newentry + 1, &new_bh);
if (!epnew) {
brelse(old_bh);
return -EIO;
@ -1061,6 +1196,7 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
exfat_remove_entries(inode, p_dir, oldentry, 0,
num_old_entries);
ei->dir = *p_dir;
ei->entry = newentry;
} else {
if (exfat_get_entry_type(epold) == TYPE_FILE) {
@ -1085,12 +1221,11 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei)
{
int ret, newentry, num_new_entries, num_old_entries;
sector_t sector_mov, sector_new;
struct exfat_dentry *epmov, *epnew;
struct super_block *sb = inode->i_sb;
struct buffer_head *mov_bh, *new_bh;
epmov = exfat_get_dentry(sb, p_olddir, oldentry, &mov_bh, &sector_mov);
epmov = exfat_get_dentry(sb, p_olddir, oldentry, &mov_bh);
if (!epmov)
return -EIO;
@ -1108,7 +1243,7 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
if (newentry < 0)
return newentry; /* -EIO or -ENOSPC */
epnew = exfat_get_dentry(sb, p_newdir, newentry, &new_bh, &sector_new);
epnew = exfat_get_dentry(sb, p_newdir, newentry, &new_bh);
if (!epnew)
return -EIO;
@ -1121,12 +1256,10 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
brelse(mov_bh);
brelse(new_bh);
epmov = exfat_get_dentry(sb, p_olddir, oldentry + 1, &mov_bh,
&sector_mov);
epmov = exfat_get_dentry(sb, p_olddir, oldentry + 1, &mov_bh);
if (!epmov)
return -EIO;
epnew = exfat_get_dentry(sb, p_newdir, newentry + 1, &new_bh,
&sector_new);
epnew = exfat_get_dentry(sb, p_newdir, newentry + 1, &new_bh);
if (!epnew) {
brelse(mov_bh);
return -EIO;
@ -1151,28 +1284,6 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
return 0;
}
static void exfat_update_parent_info(struct exfat_inode_info *ei,
struct inode *parent_inode)
{
struct exfat_sb_info *sbi = EXFAT_SB(parent_inode->i_sb);
struct exfat_inode_info *parent_ei = EXFAT_I(parent_inode);
loff_t parent_isize = i_size_read(parent_inode);
/*
* the problem that struct exfat_inode_info caches wrong parent info.
*
* because of flag-mismatch of ei->dir,
* there is abnormal traversing cluster chain.
*/
if (unlikely(parent_ei->flags != ei->dir.flags ||
parent_isize != EXFAT_CLU_TO_B(ei->dir.size, sbi) ||
parent_ei->start_clu != ei->dir.dir)) {
exfat_chain_set(&ei->dir, parent_ei->start_clu,
EXFAT_B_TO_CLU_ROUND_UP(parent_isize, sbi),
parent_ei->flags);
}
}
/* rename or move a old file into a new file */
static int __exfat_rename(struct inode *old_parent_inode,
struct exfat_inode_info *ei, struct inode *new_parent_inode,
@ -1203,12 +1314,10 @@ static int __exfat_rename(struct inode *old_parent_inode,
return -ENOENT;
}
exfat_update_parent_info(ei, old_parent_inode);
exfat_chain_dup(&olddir, &ei->dir);
dentry = ei->entry;
ep = exfat_get_dentry(sb, &olddir, dentry, &old_bh, NULL);
ep = exfat_get_dentry(sb, &olddir, dentry, &old_bh);
if (!ep) {
ret = -EIO;
goto out;
@ -1225,11 +1334,9 @@ static int __exfat_rename(struct inode *old_parent_inode,
goto out;
}
exfat_update_parent_info(new_ei, new_parent_inode);
p_dir = &(new_ei->dir);
new_entry = new_ei->entry;
ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh, NULL);
ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh);
if (!ep)
goto out;
@ -1269,7 +1376,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
if (!ret && new_inode) {
/* delete entries of new_dir */
ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh, NULL);
ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh);
if (!ep) {
ret = -EIO;
goto del_out;
@ -1313,20 +1420,32 @@ static int __exfat_rename(struct inode *old_parent_inode,
*/
new_ei->dir.dir = DIR_DELETED;
}
exfat_clear_volume_dirty(sb);
out:
return ret;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
static int exfat_rename(struct user_namespace *mnt_userns,
struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags)
#else
#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
#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.
@ -1334,6 +1453,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;
@ -1343,9 +1463,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
exfat_truncate_atime(&new_dir->i_atime);
if (IS_DIRSYNC(new_dir))
exfat_sync_inode(new_dir);
@ -1367,8 +1496,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
@ -1386,8 +1523,13 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
exfat_warn(sb, "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:
@ -1404,4 +1546,4 @@ const struct inode_operations exfat_dir_inode_operations = {
.rename = exfat_rename,
.setattr = exfat_setattr,
.getattr = exfat_getattr,
};
};

View File

@ -3,9 +3,13 @@
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
*/
#include <linux/version.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/buffer_head.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
#include <linux/vmalloc.h>
#endif
#include <asm/unaligned.h>
#include "exfat_raw.h"
@ -659,7 +663,11 @@ static int exfat_load_upcase_table(struct super_block *sb,
unsigned char skip = false;
unsigned short *upcase_table;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
upcase_table = kvcalloc(UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL);
#else
upcase_table = vzalloc(UTBL_COUNT * sizeof(unsigned short));
#endif
if (!upcase_table)
return -ENOMEM;
@ -715,7 +723,11 @@ static int exfat_load_default_upcase_table(struct super_block *sb)
unsigned short uni = 0, *upcase_table;
unsigned int index = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
upcase_table = kvcalloc(UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL);
#else
upcase_table = vzalloc(UTBL_COUNT * sizeof(unsigned short));
#endif
if (!upcase_table)
return -ENOMEM;
@ -761,7 +773,7 @@ int exfat_create_upcase_table(struct super_block *sb)
while (clu.dir != EXFAT_EOF_CLUSTER) {
for (i = 0; i < sbi->dentries_per_clu; i++) {
ep = exfat_get_dentry(sb, &clu, i, &bh, NULL);
ep = exfat_get_dentry(sb, &clu, i, &bh);
if (!ep)
return -EIO;
@ -803,5 +815,9 @@ int exfat_create_upcase_table(struct super_block *sb)
void exfat_free_upcase_table(struct exfat_sb_info *sbi)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
kvfree(sbi->vol_utbl);
#else
vfree(sbi->vol_utbl);
#endif
}

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, 6, 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;
@ -100,7 +113,6 @@ static int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flags)
{
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
bool sync;
/* retain persistent-flags */
new_flags |= sbi->vol_flags_persistent;
@ -114,21 +126,24 @@ static int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flags)
/* 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;
p_boot->vol_flags = cpu_to_le16(new_flags);
if ((new_flags & VOLUME_DIRTY) && !buffer_dirty(sbi->boot_bh))
sync = true;
else
sync = false;
set_buffer_uptodate(sbi->boot_bh);
mark_buffer_dirty(sbi->boot_bh);
if (sync)
sync_dirty_buffer(sbi->boot_bh);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
__sync_dirty_buffer(sbi->boot_bh, REQ_SYNC | REQ_FUA | REQ_PREFLUSH);
#else
__sync_dirty_buffer(sbi->boot_bh, WRITE_FLUSH_FUA);
#endif
return 0;
}
@ -174,7 +189,11 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root)
seq_puts(m, ",errors=remount-ro");
if (opts->discard)
seq_puts(m, ",discard");
if (opts->time_offset)
if (opts->keep_last_dots)
seq_puts(m, ",keep_last_dots");
if (opts->sys_tz)
seq_puts(m, ",sys_tz");
else if (opts->time_offset)
seq_printf(m, ",time_offset=%d", opts->time_offset);
return 0;
}
@ -183,7 +202,11 @@ static struct inode *exfat_alloc_inode(struct super_block *sb)
{
struct exfat_inode_info *ei;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
ei = alloc_inode_sb(sb, exfat_inode_cachep, GFP_NOFS);
#else
ei = kmem_cache_alloc(exfat_inode_cachep, GFP_NOFS);
#endif
if (!ei)
return NULL;
@ -191,14 +214,48 @@ 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
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)
static int exfat_remount(struct super_block *sb, int *flags, char *data)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
*flags |= SB_NODIRATIME;
#else
*flags |= MS_NODIRATIME;
#endif
sync_filesystem(sb);
return 0;
}
#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
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)
.remount_fs = exfat_remount,
#endif
.write_inode = exfat_write_inode,
.evict_inode = exfat_evict_inode,
.put_super = exfat_put_super,
@ -207,6 +264,7 @@ static const struct super_operations exfat_sops = {
.show_options = exfat_show_options,
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
enum {
Opt_uid,
Opt_gid,
@ -217,10 +275,38 @@ enum {
Opt_charset,
Opt_errors,
Opt_discard,
Opt_keep_last_dots,
Opt_sys_tz,
Opt_time_offset,
/* Deprecated options */
Opt_utf8,
Opt_debug,
Opt_namecase,
Opt_codepage,
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
static const struct constant_table exfat_param_enums[] = {
{ "continue", EXFAT_ERRORS_CONT },
{ "panic", EXFAT_ERRORS_PANIC },
{ "remount-ro", EXFAT_ERRORS_RO },
{}
};
#else
static const struct fs_parameter_enum exfat_param_enums[] = {
{ Opt_errors, "continue", EXFAT_ERRORS_CONT },
{ Opt_errors, "panic", EXFAT_ERRORS_PANIC },
{ Opt_errors, "remount-ro", EXFAT_ERRORS_RO },
{}
};
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
static const struct fs_parameter_spec exfat_parameters[] = {
#else
static const struct fs_parameter_spec exfat_param_specs[] = {
#endif
fsparam_u32("uid", Opt_uid),
fsparam_u32("gid", Opt_gid),
fsparam_u32oct("umask", Opt_umask),
@ -228,24 +314,33 @@ static const struct fs_parameter_spec exfat_param_specs[] = {
fsparam_u32oct("fmask", Opt_fmask),
fsparam_u32oct("allow_utime", Opt_allow_utime),
fsparam_string("iocharset", Opt_charset),
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
fsparam_enum("errors", Opt_errors, exfat_param_enums),
#else
fsparam_enum("errors", Opt_errors),
#endif
fsparam_flag("discard", Opt_discard),
fsparam_flag("keep_last_dots", Opt_keep_last_dots),
fsparam_flag("sys_tz", Opt_sys_tz),
fsparam_s32("time_offset", Opt_time_offset),
__fsparam(NULL, "utf8", Opt_utf8, fs_param_deprecated,
NULL),
__fsparam(NULL, "debug", Opt_debug, fs_param_deprecated,
NULL),
__fsparam(fs_param_is_u32, "namecase", Opt_namecase,
fs_param_deprecated, NULL),
__fsparam(fs_param_is_u32, "codepage", Opt_codepage,
fs_param_deprecated, NULL),
{}
};
static const struct fs_parameter_enum exfat_param_enums[] = {
{ Opt_errors, "continue", EXFAT_ERRORS_CONT },
{ Opt_errors, "panic", EXFAT_ERRORS_PANIC },
{ Opt_errors, "remount-ro", EXFAT_ERRORS_RO },
{}
};
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 6, 0)
static const struct fs_parameter_description exfat_parameters = {
.name = "exfat",
.specs = exfat_param_specs,
.enums = exfat_param_enums,
};
#endif
static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
@ -254,7 +349,11 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
struct fs_parse_result result;
int opt;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
opt = fs_parse(fc, exfat_parameters, param, &result);
#else
opt = fs_parse(fc, &exfat_parameters, param, &result);
#endif
if (opt < 0)
return opt;
@ -289,6 +388,12 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
case Opt_discard:
opts->discard = 1;
break;
case Opt_keep_last_dots:
opts->keep_last_dots = 1;
break;
case Opt_sys_tz:
opts->sys_tz = 1;
break;
case Opt_time_offset:
/*
* Make the limit 24 just in case someone invents something
@ -298,12 +403,175 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
return -EINVAL;
opts->time_offset = result.int_32;
break;
case Opt_utf8:
case Opt_debug:
case Opt_namecase:
case Opt_codepage:
break;
default:
return -EINVAL;
}
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_time_offset,
/* Deprecated options */
Opt_utf8,
Opt_debug,
Opt_namecase,
Opt_codepage,
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_codepage, "codepage=%u"},
{Opt_namecase, "namecase=%u"},
{Opt_debug, "debug"},
{Opt_utf8, "utf8"},
{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;
case Opt_time_offset:
if (match_int(&args[0], &option))
return -EINVAL;
/*
* Make the limit 24 just in case someone
* invents something unusual.
*/
if (option < -24 * 60 || option > 24 * 60)
return -EINVAL;
opts->time_offset = option;
break;
case Opt_utf8:
case Opt_debug:
case Opt_namecase:
case Opt_codepage:
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)
{
@ -346,21 +614,31 @@ 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;
inode->i_fop = &exfat_dir_operations;
inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1))
& ~(sbi->cluster_size - 1)) >> inode->i_blkbits;
EXFAT_I(inode)->i_pos = ((loff_t)sbi->root_dir << 32) | 0xffffffff;
EXFAT_I(inode)->i_size_aligned = i_size_read(inode);
EXFAT_I(inode)->i_size_ondisk = i_size_read(inode);
inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) &
~((loff_t)sbi->cluster_size - 1)) >> inode->i_blkbits;
ei->i_pos = ((loff_t)sbi->root_dir << 32) | 0xffffffff;
ei->i_size_aligned = i_size_read(inode);
ei->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_truncate_atime(&inode->i_atime);
return 0;
}
@ -369,8 +647,7 @@ static int exfat_calibrate_blocksize(struct super_block *sb, int logical_sect)
{
struct exfat_sb_info *sbi = EXFAT_SB(sb);
if (!is_power_of_2(logical_sect) ||
logical_sect < 512 || logical_sect > 4096) {
if (!is_power_of_2(logical_sect)) {
exfat_err(sb, "bogus logical sector size %u", logical_sect);
return -EIO;
}
@ -439,6 +716,25 @@ static int exfat_read_boot_sector(struct super_block *sb)
return -EINVAL;
}
/*
* sect_size_bits could be at least 9 and at most 12.
*/
if (p_boot->sect_size_bits < EXFAT_MIN_SECT_SIZE_BITS ||
p_boot->sect_size_bits > EXFAT_MAX_SECT_SIZE_BITS) {
exfat_err(sb, "bogus sector size bits : %u\n",
p_boot->sect_size_bits);
return -EINVAL;
}
/*
* sect_per_clus_bits could be at least 0 and at most 25 - sect_size_bits.
*/
if (p_boot->sect_per_clus_bits > EXFAT_MAX_SECT_PER_CLUS_BITS(p_boot)) {
exfat_err(sb, "bogus sectors bits per cluster : %u\n",
p_boot->sect_per_clus_bits);
return -EINVAL;
}
sbi->sect_per_clus = 1 << p_boot->sect_per_clus_bits;
sbi->sect_per_clus_bits = p_boot->sect_per_clus_bits;
sbi->cluster_size_bits = p_boot->sect_per_clus_bits +
@ -465,16 +761,19 @@ static int exfat_read_boot_sector(struct super_block *sb)
sbi->used_clusters = EXFAT_CLUSTERS_UNTRACKED;
/* check consistencies */
if (sbi->num_FAT_sectors << p_boot->sect_size_bits <
sbi->num_clusters * 4) {
if ((u64)sbi->num_FAT_sectors << p_boot->sect_size_bits <
(u64)sbi->num_clusters * 4) {
exfat_err(sb, "bogus fat length");
return -EINVAL;
}
if (sbi->data_start_sector <
sbi->FAT1_start_sector + sbi->num_FAT_sectors * p_boot->num_fats) {
(u64)sbi->FAT1_start_sector +
(u64)sbi->num_FAT_sectors * p_boot->num_fats) {
exfat_err(sb, "bogus data start sector");
return -EINVAL;
}
if (sbi->vol_flags & VOLUME_DIRTY)
exfat_warn(sb, "Volume was not properly unmounted. Some data may be corrupt. Please run fsck.");
if (sbi->vol_flags & MEDIA_FAILURE)
@ -582,12 +881,17 @@ static int __exfat_fill_super(struct super_block *sb)
return ret;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 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, 6, 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;
@ -600,14 +904,44 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
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);
mutex_init(&sbi->bitmap_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 = 10 * NSEC_PER_MSEC;
sb->s_time_min = EXFAT_MIN_TIMESTAMP_SECS;
sb->s_time_max = EXFAT_MAX_TIMESTAMP_SECS;
#endif
err = __exfat_fill_super(sb);
if (err) {
@ -619,7 +953,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, 6, 0)
opts->utf8 = 1;
#else
sbi->options.utf8 = 1;
#endif
else {
sbi->nls_io = load_nls(sbi->options.iocharset);
if (!sbi->nls_io) {
@ -643,7 +981,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_err(sb, "failed to initialize root inode");
@ -657,7 +1000,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
if (!sb->s_root) {
exfat_err(sb, "failed to get the root dentry");
err = -ENOMEM;
goto put_inode;
goto free_table;
}
return 0;
@ -679,6 +1022,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
return err;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
static int exfat_get_tree(struct fs_context *fc)
{
return get_tree_bdev(fc, exfat_fill_super);
@ -719,6 +1063,7 @@ static int exfat_init_fs_context(struct fs_context *fc)
return -ENOMEM;
mutex_init(&sbi->s_lock);
mutex_init(&sbi->bitmap_lock);
ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
@ -734,12 +1079,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, 6, 0)
.init_fs_context = exfat_init_fs_context,
.parameters = &exfat_parameters,
.parameters = exfat_parameters,
#else
.mount = exfat_fs_mount,
#endif
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
@ -805,3 +1161,4 @@ MODULE_ALIAS_FS("exfat");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("exFAT filesystem support");
MODULE_AUTHOR("Samsung Electronics Co., Ltd.");
MODULE_VERSION(EXFAT_VERSION);