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:
commit
566743e4e4
65
fs/exfat/.travis_cmd_wrapper.pl
Executable file
65
fs/exfat/.travis_cmd_wrapper.pl
Executable 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]);
|
37
fs/exfat/.travis_get_mainline_kernel
Executable file
37
fs/exfat/.travis_get_mainline_kernel
Executable 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
|
@ -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.
|
||||
|
@ -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
53
fs/exfat/README.md
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -311,4 +311,4 @@ int exfat_get_cluster(struct inode *inode, unsigned int cluster,
|
||||
|
||||
exfat_cache_add(inode, &cid);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -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, §or);
|
||||
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, §or);
|
||||
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, §or);
|
||||
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, §or);
|
||||
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, §or);
|
||||
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, §or);
|
||||
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, §or);
|
||||
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, §or);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
146
fs/exfat/file.c
146
fs/exfat/file.c
@ -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,
|
||||
};
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
298
fs/exfat/namei.c
298
fs/exfat/namei.c
@ -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, §or);
|
||||
&(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, §or);
|
||||
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, §or);
|
||||
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, §or_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,
|
||||
§or_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,
|
||||
§or_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,
|
||||
§or_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, §or_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, §or_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,
|
||||
§or_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,
|
||||
§or_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,
|
||||
};
|
||||
};
|
||||
|
@ -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
|
||||
}
|
||||
|
419
fs/exfat/super.c
419
fs/exfat/super.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user