Merge branch 'master' of https://github.com/namjaejeon/linux-exfat-oot into android13-5.4-lahaina
* 'master' of https://github.com/namjaejeon/linux-exfat-oot: exfat: add necessary header for vmalloc exfat: release s_lock before calling dir_emit() exfat: check if filename entries exceeds max filename length exfat: github action: make space for running xfstests exfat: use kvmalloc_array/kvfree instead of kmalloc_array/kfree exfat: splice: Use filemap_splice_read() instead of generic_file_splice_read() exfat: fs: build the legacy direct I/O code conditionally exfat: fs: port ->rename() to pass mnt_idmap exfat: fs: port ->mkdir() to pass mnt_idmap exfat: fs: port ->create() to pass mnt_idmap exfat: fs: port ->getattr() to pass mnt_idmap exfat: fs: port ->setattr() to pass mnt_idmap exfat: fix the newly allocated clusters are not freed in error handling exfat: don't print error log in normal case exfat: remove unneeded code from exfat_alloc_cluster() exfat: remove ->writepage exfat: handle unreconized benign secondary entries exfat: fix inode->i_blocks for non-512 byte sector size device exfat: redefine DIR_DELETED as the bad cluster number exfat: fix reporting fs error when reading dir beyond EOF exfat: fix unexpected EOF while reading dir exfat: reuse exfat_find_location() to simplify exfat_get_dentry_set() exfat: fix overflow in sector and cluster conversion exfat: remove i_size_write() from __exfat_truncate() exfat: remove argument 'size' from exfat_truncate() exfat: remove unnecessary arguments from exfat_find_dir_entry() exfat: remove unneeded codes from __exfat_rename() exfat: remove call ilog2() from exfat_readdir() exfat: remove generic/286 exfat: fix python package installation failure exfat: github actions: add apt-get update command exfat: treewide: use get_random_u32() when possible exfat: replace magic numbers with Macros exfat: rename exfat_free_dentry_set() to exfat_put_dentry_set() exfat: move exfat_entry_set_cache from heap to stack exfat: support dynamic allocate bh for exfat_entry_set_cache exfat: reduce the size of exfat_entry_set_cache exfat: add SECTOR_SIZE macro exfat: hint the empty entry which at the end of cluster chain exfat: simplify empty entry hint exfat: add auto-test using github action exfat: remove travis-CI test exfat: release 6.0.0 version exfat: fix overflow for large capacity partition exfat: add auto build-test and simple stability test using travis-CI exfat: Drop superfluous new line for error messages exfat: Downgrade ENAMETOOLONG error message to debug messages exfat: Expand exfat_err() and co directly to pr_*() macro exfat: Define NLS_NAME_* as bit flags explicitly exfat: Return ENAMETOOLONG consistently for oversized paths exfat: simplified by using round_up() fs: Convert mpage_readpage to mpage_read_folio fs: Remove flags parameter from aops->write_begin fs: Remove aop flags parameter from cont_write_begin() block: add a bdev_discard_granularity helper block: remove QUEUE_FLAG_DISCARD exfat: remove duplicate write inode for extending dir/file exfat: remove duplicate write inode for truncating file exfat: reuse __exfat_write_inode() to update directory entry exfat: use updated exfat_chain directly during renaming Change-Id: Ib68d90de9ea596296407f446fa611525034db193
This commit is contained in:
commit
1cbc6feae2
196
fs/exfat/.github/workflows/c-cpp.yml
vendored
Normal file
196
fs/exfat/.github/workflows/c-cpp.yml
vendored
Normal file
@ -0,0 +1,196 @@
|
||||
name: linux-exfat-oot CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Download the kernel
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install libelf-dev wget tar gzip python2.7
|
||||
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.1.36.tar.gz
|
||||
tar xf linux-4.1.36.tar.gz
|
||||
mv linux-4.1.36 linux-stable
|
||||
- name: Prerequisite for xfstests testing
|
||||
run: |
|
||||
sudo apt-get install linux-headers-$(uname -r)
|
||||
sudo apt-get install autoconf libtool pkg-config libnl-3-dev libnl-genl-3-dev
|
||||
sudo apt-get install xfslibs-dev uuid-dev libtool-bin xfsprogs libgdbm-dev gawk fio attr libattr1-dev libacl1-dev libaio-dev
|
||||
git clone --branch=exfat-next https://github.com/exfat-utils/exfat-utils
|
||||
git clone https://github.com/namjaejeon/exfat-testsuites
|
||||
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
|
||||
export PATH=/usr/local/lib:$PATH
|
||||
sudo useradd fsgqa
|
||||
sudo useradd 123456-fsgqa
|
||||
- name: Copy exfat source to kernel
|
||||
run: |
|
||||
mv linux-stable ../
|
||||
mkdir ../linux-stable/fs/exfat
|
||||
cp -ar * ../linux-stable/fs/exfat/
|
||||
- name: Compile with 4.1 kernel
|
||||
run: |
|
||||
cd ../linux-stable
|
||||
yes "" | make oldconfig > /dev/null
|
||||
echo 'obj-$(CONFIG_EXFAT_FS) += exfat/' >> fs/Makefile
|
||||
echo 'source "fs/exfat/Kconfig"' >> fs/Kconfig
|
||||
echo 'CONFIG_EXFAT_FS=m' >> .config
|
||||
echo 'CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8"' >> .config
|
||||
make -j$((`nproc`+1)) fs/exfat/exfat.ko
|
||||
- name: Run xfstests testsuite
|
||||
run: |
|
||||
cd ../linux-exfat-oot
|
||||
make > /dev/null
|
||||
sudo make install > /dev/null
|
||||
sudo insmod exfat.ko
|
||||
cd exfat-utils
|
||||
./autogen.sh > /dev/null
|
||||
./configure > /dev/null
|
||||
make -j$((`nproc`+1)) > /dev/null
|
||||
sudo make install > /dev/null
|
||||
cd ..
|
||||
sudo mkdir -p /mnt/scratch
|
||||
sudo mkdir -p /mnt/test
|
||||
sudo mkdir -p full_test
|
||||
- name: create file/director test
|
||||
run: |
|
||||
truncate -s 10G full_test.img
|
||||
sudo losetup /dev/loop22 full_test.img
|
||||
sudo mkfs.exfat /dev/loop22
|
||||
sudo mount -t exfat /dev/loop22 ./full_test/
|
||||
cd full_test/
|
||||
i=1;while [ $i -le 10000 ];do sudo touch file$i;if [ $? != 0 ]; then exit 1; fi; i=$(($i + 1));done
|
||||
sync
|
||||
sudo fsck.exfat /dev/loop22
|
||||
sudo rm -rf *
|
||||
i=1;while [ $i -le 10000 ];do sudo mkdir file$i;if [ $? != 0 ]; then exit 1; fi; i=$(($i + 1));done
|
||||
sync
|
||||
sudo rm -rf *
|
||||
sudo fsck.exfat /dev/loop22
|
||||
cd ..
|
||||
sudo umount ./full_test/
|
||||
sudo fsck.exfat /dev/loop22
|
||||
sudo losetup -d /dev/loop22
|
||||
rm full_test.img
|
||||
- name: xfstest tests
|
||||
run: |
|
||||
cd exfat-testsuites/
|
||||
tar xzvf xfstests-exfat.tgz > /dev/null
|
||||
cd xfstests-exfat
|
||||
make -j$((`nproc`+1)) > /dev/null
|
||||
truncate -s 100G test.img
|
||||
truncate -s 100G scratch.img
|
||||
sudo losetup /dev/loop20 test.img
|
||||
sudo losetup /dev/loop21 scratch.img
|
||||
sudo mkfs.exfat /dev/loop20
|
||||
sudo mkfs.exfat /dev/loop21
|
||||
sudo ./check generic/001
|
||||
sudo ./check generic/006
|
||||
sudo ./check generic/007
|
||||
sudo ./check generic/011
|
||||
sudo ./check generic/013
|
||||
sudo ./check generic/014
|
||||
sudo ./check generic/028
|
||||
sudo ./check generic/029
|
||||
sudo ./check generic/030
|
||||
sudo ./check generic/034
|
||||
sudo ./check generic/035
|
||||
sudo ./check generic/036
|
||||
sudo ./check generic/069
|
||||
sudo ./check generic/073
|
||||
sudo ./check generic/074
|
||||
sudo ./check generic/075
|
||||
sudo ./check generic/076
|
||||
sudo ./check generic/080
|
||||
sudo ./check generic/084
|
||||
sudo ./check generic/091
|
||||
sudo ./check generic/095
|
||||
sudo ./check generic/098
|
||||
sudo ./check generic/100
|
||||
sudo ./check generic/112
|
||||
sudo ./check generic/113
|
||||
sudo ./check generic/114
|
||||
sudo ./check generic/120
|
||||
sudo ./check generic/123
|
||||
sudo ./check generic/124
|
||||
sudo ./check generic/127
|
||||
sudo ./check generic/129
|
||||
sudo ./check generic/130
|
||||
sudo ./check generic/131
|
||||
sudo ./check generic/132
|
||||
sudo ./check generic/133
|
||||
sudo ./check generic/135
|
||||
sudo ./check generic/141
|
||||
sudo ./check generic/169
|
||||
sudo ./check generic/198
|
||||
sudo ./check generic/207
|
||||
sudo ./check generic/208
|
||||
sudo ./check generic/209
|
||||
sudo ./check generic/210
|
||||
sudo ./check generic/211
|
||||
sudo ./check generic/212
|
||||
sudo ./check generic/215
|
||||
sudo losetup -d /dev/loop20
|
||||
sudo losetup -d /dev/loop21
|
||||
rm test.img
|
||||
rm scratch.img
|
||||
truncate -s 100G test.img
|
||||
truncate -s 100G scratch.img
|
||||
sudo losetup /dev/loop20 test.img
|
||||
sudo losetup /dev/loop21 scratch.img
|
||||
sudo mkfs.exfat /dev/loop20
|
||||
sudo mkfs.exfat /dev/loop21
|
||||
sudo ./check generic/221
|
||||
sudo ./check generic/239
|
||||
sudo ./check generic/240
|
||||
sudo ./check generic/241
|
||||
sudo ./check generic/245
|
||||
sudo ./check generic/246
|
||||
sudo ./check generic/247
|
||||
sudo ./check generic/248
|
||||
sudo ./check generic/249
|
||||
sudo ./check generic/257
|
||||
sudo ./check generic/260
|
||||
sudo ./check generic/263
|
||||
sudo ./check generic/285
|
||||
sudo ./check generic/288
|
||||
sudo ./check generic/308
|
||||
sudo ./check generic/309
|
||||
sudo ./check generic/310
|
||||
sudo ./check generic/313
|
||||
sudo ./check generic/323
|
||||
sudo ./check generic/325
|
||||
sudo ./check generic/338
|
||||
sudo ./check generic/339
|
||||
sudo ./check generic/340
|
||||
sudo ./check generic/344
|
||||
sudo ./check generic/345
|
||||
sudo ./check generic/346
|
||||
sudo ./check generic/354
|
||||
sudo ./check generic/376
|
||||
sudo ./check generic/393
|
||||
sudo ./check generic/394
|
||||
sudo ./check generic/405
|
||||
sudo ./check generic/406
|
||||
sudo ./check generic/409
|
||||
sudo ./check generic/410
|
||||
sudo ./check generic/411
|
||||
sudo ./check generic/412
|
||||
sudo ./check generic/418
|
||||
sudo ./check generic/428
|
||||
sudo ./check generic/437
|
||||
sudo ./check generic/438
|
||||
sudo ./check generic/441
|
||||
sudo ./check generic/443
|
||||
sudo ./check generic/448
|
||||
sudo ./check generic/450
|
||||
sudo ./check generic/451
|
||||
sudo ./check generic/452
|
@ -1,65 +0,0 @@
|
||||
#!/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]);
|
@ -1,37 +0,0 @@
|
||||
#!/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
|
@ -3,6 +3,7 @@
|
||||
config EXFAT_FS
|
||||
tristate "exFAT filesystem support"
|
||||
select NLS
|
||||
select LEGACY_DIRECT_IO
|
||||
help
|
||||
This allows you to mount devices formatted with the exFAT file system.
|
||||
exFAT is typically used on SD-Cards or USB sticks.
|
||||
|
@ -12,6 +12,7 @@
|
||||
#else
|
||||
#include <linux/sched.h>
|
||||
#endif
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include "exfat_raw.h"
|
||||
#include "exfat_fs.h"
|
||||
@ -75,8 +76,12 @@ static int exfat_allocate_bitmap(struct super_block *sb,
|
||||
}
|
||||
sbi->map_sectors = ((need_map_size - 1) >>
|
||||
(sb->s_blocksize_bits)) + 1;
|
||||
sbi->vol_amap = kmalloc_array(sbi->map_sectors,
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
|
||||
sbi->vol_amap = kvmalloc_array(sbi->map_sectors,
|
||||
sizeof(struct buffer_head *), GFP_KERNEL);
|
||||
#else
|
||||
sbi->vol_amap = vmalloc(sbi->map_sectors * sizeof(struct buffer_head *));
|
||||
#endif
|
||||
if (!sbi->vol_amap)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -90,7 +95,7 @@ static int exfat_allocate_bitmap(struct super_block *sb,
|
||||
while (j < i)
|
||||
brelse(sbi->vol_amap[j++]);
|
||||
|
||||
kfree(sbi->vol_amap);
|
||||
kvfree(sbi->vol_amap);
|
||||
sbi->vol_amap = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
@ -144,7 +149,7 @@ void exfat_free_bitmap(struct exfat_sb_info *sbi)
|
||||
for (i = 0; i < sbi->map_sectors; i++)
|
||||
__brelse(sbi->vol_amap[i]);
|
||||
|
||||
kfree(sbi->vol_amap);
|
||||
kvfree(sbi->vol_amap);
|
||||
}
|
||||
|
||||
int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync)
|
||||
|
302
fs/exfat/dir.c
302
fs/exfat/dir.c
@ -30,15 +30,16 @@ static int exfat_extract_uni_name(struct exfat_dentry *ep,
|
||||
|
||||
}
|
||||
|
||||
static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
|
||||
static int exfat_get_uniname_from_ext_entry(struct super_block *sb,
|
||||
struct exfat_chain *p_dir, int entry, unsigned short *uniname)
|
||||
{
|
||||
int i;
|
||||
struct exfat_entry_set_cache *es;
|
||||
int i, err;
|
||||
struct exfat_entry_set_cache es;
|
||||
unsigned int uni_len = 0, len;
|
||||
|
||||
es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
|
||||
if (!es)
|
||||
return;
|
||||
err = exfat_get_dentry_set(&es, sb, p_dir, entry, ES_ALL_ENTRIES);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* First entry : file entry
|
||||
@ -46,24 +47,28 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
|
||||
* Third entry : first file-name entry
|
||||
* So, the index of first file-name dentry should start from 2.
|
||||
*/
|
||||
for (i = 2; i < es->num_entries; i++) {
|
||||
struct exfat_dentry *ep = exfat_get_dentry_cached(es, i);
|
||||
for (i = ES_IDX_FIRST_FILENAME; i < es.num_entries; i++) {
|
||||
struct exfat_dentry *ep = exfat_get_dentry_cached(&es, i);
|
||||
|
||||
/* end of name entry */
|
||||
if (exfat_get_entry_type(ep) != TYPE_EXTEND)
|
||||
break;
|
||||
|
||||
exfat_extract_uni_name(ep, uniname);
|
||||
len = exfat_extract_uni_name(ep, uniname);
|
||||
uni_len += len;
|
||||
if (len != EXFAT_FILE_NAME_LEN || uni_len >= MAX_NAME_LENGTH)
|
||||
break;
|
||||
uniname += EXFAT_FILE_NAME_LEN;
|
||||
}
|
||||
|
||||
exfat_free_dentry_set(es, false);
|
||||
exfat_put_dentry_set(&es, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read a directory entry from the opened directory */
|
||||
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;
|
||||
int i, dentries_per_clu, num_ext, err;
|
||||
unsigned int type, clu_offset, max_dentries;
|
||||
struct exfat_chain dir, clu;
|
||||
struct exfat_uni_name uni_name;
|
||||
@ -85,11 +90,10 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
|
||||
EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags);
|
||||
|
||||
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);
|
||||
(u64)EXFAT_CLU_TO_DEN(sbi->num_clusters, sbi));
|
||||
|
||||
clu_offset = dentry >> dentries_per_clu_bits;
|
||||
clu_offset = EXFAT_DEN_TO_CLU(dentry, sbi);
|
||||
exfat_chain_dup(&clu, &dir);
|
||||
|
||||
if (clu.flags == ALLOC_NO_FAT_CHAIN) {
|
||||
@ -103,7 +107,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
|
||||
clu.dir = ei->hint_bmap.clu;
|
||||
}
|
||||
|
||||
while (clu_offset > 0) {
|
||||
while (clu_offset > 0 && clu.dir != EXFAT_EOF_CLUSTER) {
|
||||
if (exfat_get_next_cluster(sb, &(clu.dir)))
|
||||
return -EIO;
|
||||
|
||||
@ -149,8 +153,12 @@ 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, &clu, i,
|
||||
err = exfat_get_uniname_from_ext_entry(sb, &clu, i,
|
||||
uni_name.name);
|
||||
if (err) {
|
||||
brelse(bh);
|
||||
continue;
|
||||
}
|
||||
exfat_utf16_to_nls(sb, &uni_name,
|
||||
dir_entry->namebuf.lfn,
|
||||
dir_entry->namebuf.lfnbuf_len);
|
||||
@ -164,7 +172,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
|
||||
dir_entry->entry = dentry;
|
||||
brelse(bh);
|
||||
|
||||
ei->hint_bmap.off = dentry >> dentries_per_clu_bits;
|
||||
ei->hint_bmap.off = EXFAT_DEN_TO_CLU(dentry, sbi);
|
||||
ei->hint_bmap.clu = clu.dir;
|
||||
|
||||
*cpos = EXFAT_DEN_TO_B(dentry + 1 + num_ext);
|
||||
@ -211,7 +219,10 @@ static void exfat_free_namebuf(struct exfat_dentry_namebuf *nb)
|
||||
exfat_init_namebuf(nb);
|
||||
}
|
||||
|
||||
/* skip iterating emit_dots when dir is empty */
|
||||
/*
|
||||
* Before calling dir_emit*(), sbi->s_lock should be released
|
||||
* because page fault can occur in dir_emit*().
|
||||
*/
|
||||
#define ITER_POS_FILLED_DOTS (2)
|
||||
static int exfat_iterate(struct file *filp, struct dir_context *ctx)
|
||||
{
|
||||
@ -226,35 +237,33 @@ static int exfat_iterate(struct file *filp, struct dir_context *ctx)
|
||||
int err = 0, fake_offset = 0;
|
||||
|
||||
exfat_init_namebuf(nb);
|
||||
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
||||
|
||||
cpos = ctx->pos;
|
||||
if (!dir_emit_dots(filp, ctx))
|
||||
goto unlock;
|
||||
goto out;
|
||||
|
||||
if (ctx->pos == ITER_POS_FILLED_DOTS) {
|
||||
cpos = 0;
|
||||
fake_offset = 1;
|
||||
}
|
||||
|
||||
if (cpos & (DENTRY_SIZE - 1)) {
|
||||
err = -ENOENT;
|
||||
goto unlock;
|
||||
}
|
||||
cpos = round_up(cpos, DENTRY_SIZE);
|
||||
|
||||
/* name buffer should be allocated before use */
|
||||
err = exfat_alloc_namebuf(nb);
|
||||
if (err)
|
||||
goto unlock;
|
||||
goto out;
|
||||
get_new:
|
||||
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
||||
|
||||
if (ei->flags == ALLOC_NO_FAT_CHAIN && cpos >= i_size_read(inode))
|
||||
goto end_of_dir;
|
||||
|
||||
err = exfat_readdir(inode, &cpos, &de);
|
||||
if (err) {
|
||||
/*
|
||||
* At least we tried to read a sector. Move cpos to next sector
|
||||
* position (should be aligned).
|
||||
* At least we tried to read a sector.
|
||||
* Move cpos to next sector position (should be aligned).
|
||||
*/
|
||||
if (err == -EIO) {
|
||||
cpos += 1 << (sb->s_blocksize_bits);
|
||||
@ -277,16 +286,10 @@ get_new:
|
||||
inum = iunique(sb, EXFAT_ROOT_INO);
|
||||
}
|
||||
|
||||
/*
|
||||
* Before calling dir_emit(), sb_lock should be released.
|
||||
* Because page fault can occur in dir_emit() when the size
|
||||
* of buffer given from user is larger than one page size.
|
||||
*/
|
||||
mutex_unlock(&EXFAT_SB(sb)->s_lock);
|
||||
if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum,
|
||||
(de.attr & ATTR_SUBDIR) ? DT_DIR : DT_REG))
|
||||
goto out_unlocked;
|
||||
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
||||
goto out;
|
||||
ctx->pos = cpos;
|
||||
goto get_new;
|
||||
|
||||
@ -294,9 +297,8 @@ end_of_dir:
|
||||
if (!cpos && fake_offset)
|
||||
cpos = ITER_POS_FILLED_DOTS;
|
||||
ctx->pos = cpos;
|
||||
unlock:
|
||||
mutex_unlock(&EXFAT_SB(sb)->s_lock);
|
||||
out_unlocked:
|
||||
out:
|
||||
/*
|
||||
* To improve performance, free namebuf after unlock sb_lock.
|
||||
* If namebuf is not allocated, this function do nothing
|
||||
@ -338,7 +340,7 @@ int exfat_calc_num_entries(struct exfat_uni_name *p_uniname)
|
||||
return -EINVAL;
|
||||
|
||||
/* 1 file entry + 1 stream entry + name entries */
|
||||
return ((len - 1) / EXFAT_FILE_NAME_LEN + 3);
|
||||
return ES_ENTRY_NUM(len);
|
||||
}
|
||||
|
||||
unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
|
||||
@ -381,6 +383,12 @@ unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
|
||||
return TYPE_ACL;
|
||||
return TYPE_CRITICAL_SEC;
|
||||
}
|
||||
|
||||
if (ep->type == EXFAT_VENDOR_EXT)
|
||||
return TYPE_VENDOR_EXT;
|
||||
if (ep->type == EXFAT_VENDOR_ALLOC)
|
||||
return TYPE_VENDOR_ALLOC;
|
||||
|
||||
return TYPE_BENIGN_SEC;
|
||||
}
|
||||
|
||||
@ -534,6 +542,25 @@ release_fbh:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exfat_free_benign_secondary_clusters(struct inode *inode,
|
||||
struct exfat_dentry *ep)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_chain dir;
|
||||
unsigned int start_clu =
|
||||
le32_to_cpu(ep->dentry.generic_secondary.start_clu);
|
||||
u64 size = le64_to_cpu(ep->dentry.generic_secondary.size);
|
||||
unsigned char flags = ep->dentry.generic_secondary.flags;
|
||||
|
||||
if (!(flags & ALLOC_POSSIBLE) || !start_clu || !size)
|
||||
return;
|
||||
|
||||
exfat_chain_set(&dir, start_clu,
|
||||
EXFAT_B_TO_CLU_ROUND_UP(size, EXFAT_SB(sb)),
|
||||
flags);
|
||||
exfat_free_cluster(inode, &dir);
|
||||
}
|
||||
|
||||
int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
|
||||
int entry, int num_entries, struct exfat_uni_name *p_uniname)
|
||||
{
|
||||
@ -566,6 +593,9 @@ int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
|
||||
if (!ep)
|
||||
return -EIO;
|
||||
|
||||
if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)
|
||||
exfat_free_benign_secondary_clusters(inode, ep);
|
||||
|
||||
exfat_init_name_entry(ep, uniname);
|
||||
exfat_update_bh(bh, sync);
|
||||
brelse(bh);
|
||||
@ -589,6 +619,9 @@ int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
|
||||
if (!ep)
|
||||
return -EIO;
|
||||
|
||||
if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)
|
||||
exfat_free_benign_secondary_clusters(inode, ep);
|
||||
|
||||
exfat_set_entry_type(ep, TYPE_DELETED);
|
||||
exfat_update_bh(bh, IS_DIRSYNC(inode));
|
||||
brelse(bh);
|
||||
@ -603,18 +636,18 @@ void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
|
||||
unsigned short chksum = 0;
|
||||
struct exfat_dentry *ep;
|
||||
|
||||
for (i = 0; i < es->num_entries; i++) {
|
||||
for (i = ES_IDX_FILE; i < es->num_entries; i++) {
|
||||
ep = exfat_get_dentry_cached(es, i);
|
||||
chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum,
|
||||
chksum_type);
|
||||
chksum_type = CS_DEFAULT;
|
||||
}
|
||||
ep = exfat_get_dentry_cached(es, 0);
|
||||
ep = exfat_get_dentry_cached(es, ES_IDX_FILE);
|
||||
ep->dentry.file.checksum = cpu_to_le16(chksum);
|
||||
es->modified = true;
|
||||
}
|
||||
|
||||
int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
|
||||
int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
||||
@ -626,7 +659,10 @@ int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
|
||||
bforget(es->bh[i]);
|
||||
else
|
||||
brelse(es->bh[i]);
|
||||
kfree(es);
|
||||
|
||||
if (IS_DYNAMIC_ES(es))
|
||||
kfree(es->bh);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -754,6 +790,7 @@ enum exfat_validate_dentry_mode {
|
||||
ES_MODE_GET_STRM_ENTRY,
|
||||
ES_MODE_GET_NAME_ENTRY,
|
||||
ES_MODE_GET_CRITICAL_SEC_ENTRY,
|
||||
ES_MODE_GET_BENIGN_SEC_ENTRY,
|
||||
};
|
||||
|
||||
static bool exfat_validate_entry(unsigned int type,
|
||||
@ -767,36 +804,33 @@ static bool exfat_validate_entry(unsigned int type,
|
||||
if (type != TYPE_FILE && type != TYPE_DIR)
|
||||
return false;
|
||||
*mode = ES_MODE_GET_FILE_ENTRY;
|
||||
return true;
|
||||
break;
|
||||
case ES_MODE_GET_FILE_ENTRY:
|
||||
if (type != TYPE_STREAM)
|
||||
return false;
|
||||
*mode = ES_MODE_GET_STRM_ENTRY;
|
||||
return true;
|
||||
break;
|
||||
case ES_MODE_GET_STRM_ENTRY:
|
||||
if (type != TYPE_EXTEND)
|
||||
return false;
|
||||
*mode = ES_MODE_GET_NAME_ENTRY;
|
||||
return true;
|
||||
break;
|
||||
case ES_MODE_GET_NAME_ENTRY:
|
||||
if (type == TYPE_STREAM)
|
||||
if (type & TYPE_BENIGN_SEC)
|
||||
*mode = ES_MODE_GET_BENIGN_SEC_ENTRY;
|
||||
else if (type != TYPE_EXTEND)
|
||||
return false;
|
||||
if (type != TYPE_EXTEND) {
|
||||
if (!(type & TYPE_CRITICAL_SEC))
|
||||
break;
|
||||
case ES_MODE_GET_BENIGN_SEC_ENTRY:
|
||||
/* Assume unreconized benign secondary entry */
|
||||
if (!(type & TYPE_BENIGN_SEC))
|
||||
return false;
|
||||
*mode = ES_MODE_GET_CRITICAL_SEC_ENTRY;
|
||||
}
|
||||
return true;
|
||||
case ES_MODE_GET_CRITICAL_SEC_ENTRY:
|
||||
if (type == TYPE_EXTEND || type == TYPE_STREAM)
|
||||
return false;
|
||||
if ((type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC)
|
||||
return false;
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct exfat_dentry *exfat_get_dentry_cached(
|
||||
@ -823,14 +857,14 @@ struct exfat_dentry *exfat_get_dentry_cached(
|
||||
* pointer of entry set on success,
|
||||
* NULL on failure.
|
||||
*/
|
||||
struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
|
||||
struct exfat_chain *p_dir, int entry, unsigned int type)
|
||||
int exfat_get_dentry_set(struct exfat_entry_set_cache *es,
|
||||
struct super_block *sb, struct exfat_chain *p_dir, int entry,
|
||||
unsigned int type)
|
||||
{
|
||||
int ret, i, num_bh;
|
||||
unsigned int off, byte_offset, clu = 0;
|
||||
unsigned int off;
|
||||
sector_t sec;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_entry_set_cache *es;
|
||||
struct exfat_dentry *ep;
|
||||
int num_entries;
|
||||
enum exfat_validate_dentry_mode mode = ES_MODE_STARTED;
|
||||
@ -838,52 +872,51 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
|
||||
|
||||
if (p_dir->dir == DIR_DELETED) {
|
||||
exfat_err(sb, "access to deleted dentry");
|
||||
return NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
byte_offset = EXFAT_DEN_TO_B(entry);
|
||||
ret = exfat_walk_fat_chain(sb, p_dir, byte_offset, &clu);
|
||||
ret = exfat_find_location(sb, p_dir, entry, &sec, &off);
|
||||
if (ret)
|
||||
return NULL;
|
||||
return ret;
|
||||
|
||||
es = kzalloc(sizeof(*es), GFP_KERNEL);
|
||||
if (!es)
|
||||
return NULL;
|
||||
memset(es, 0, sizeof(*es));
|
||||
es->sb = sb;
|
||||
es->modified = false;
|
||||
|
||||
/* byte offset in cluster */
|
||||
byte_offset = EXFAT_CLU_OFFSET(byte_offset, sbi);
|
||||
|
||||
/* byte offset in sector */
|
||||
off = EXFAT_BLK_OFFSET(byte_offset, sb);
|
||||
es->start_off = off;
|
||||
|
||||
/* sector offset in cluster */
|
||||
sec = EXFAT_B_TO_BLK(byte_offset, sb);
|
||||
sec += exfat_cluster_to_sector(sbi, clu);
|
||||
es->bh = es->__bh;
|
||||
|
||||
bh = sb_bread(sb, sec);
|
||||
if (!bh)
|
||||
goto free_es;
|
||||
return -EIO;
|
||||
es->bh[es->num_bh++] = bh;
|
||||
|
||||
ep = exfat_get_dentry_cached(es, 0);
|
||||
ep = exfat_get_dentry_cached(es, ES_IDX_FILE);
|
||||
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
|
||||
goto free_es;
|
||||
goto put_es;
|
||||
|
||||
num_entries = type == ES_ALL_ENTRIES ?
|
||||
ep->dentry.file.num_ext + 1 : type;
|
||||
es->num_entries = num_entries;
|
||||
|
||||
num_bh = EXFAT_B_TO_BLK_ROUND_UP(off + num_entries * DENTRY_SIZE, sb);
|
||||
if (num_bh > ARRAY_SIZE(es->__bh)) {
|
||||
es->bh = kmalloc_array(num_bh, sizeof(*es->bh), GFP_KERNEL);
|
||||
if (!es->bh) {
|
||||
brelse(bh);
|
||||
return -ENOMEM;
|
||||
}
|
||||
es->bh[0] = bh;
|
||||
}
|
||||
|
||||
for (i = 1; i < num_bh; i++) {
|
||||
/* get the next sector */
|
||||
if (exfat_is_last_sector_in_cluster(sbi, sec)) {
|
||||
unsigned int clu = exfat_sector_to_cluster(sbi, sec);
|
||||
|
||||
if (p_dir->flags == ALLOC_NO_FAT_CHAIN)
|
||||
clu++;
|
||||
else if (exfat_get_next_cluster(sb, &clu))
|
||||
goto free_es;
|
||||
goto put_es;
|
||||
sec = exfat_cluster_to_sector(sbi, clu);
|
||||
} else {
|
||||
sec++;
|
||||
@ -891,21 +924,51 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
|
||||
|
||||
bh = sb_bread(sb, sec);
|
||||
if (!bh)
|
||||
goto free_es;
|
||||
goto put_es;
|
||||
es->bh[es->num_bh++] = bh;
|
||||
}
|
||||
|
||||
/* validate cached dentries */
|
||||
for (i = 1; i < num_entries; i++) {
|
||||
for (i = ES_IDX_STREAM; i < num_entries; i++) {
|
||||
ep = exfat_get_dentry_cached(es, i);
|
||||
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
|
||||
goto free_es;
|
||||
goto put_es;
|
||||
}
|
||||
return es;
|
||||
return 0;
|
||||
|
||||
free_es:
|
||||
exfat_free_dentry_set(es, false);
|
||||
return NULL;
|
||||
put_es:
|
||||
exfat_put_dentry_set(es, false);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static inline void exfat_reset_empty_hint(struct exfat_hint_femp *hint_femp)
|
||||
{
|
||||
hint_femp->eidx = EXFAT_HINT_NONE;
|
||||
hint_femp->count = 0;
|
||||
}
|
||||
|
||||
static inline void exfat_set_empty_hint(struct exfat_inode_info *ei,
|
||||
struct exfat_hint_femp *candi_empty, struct exfat_chain *clu,
|
||||
int dentry, int num_entries, int entry_type)
|
||||
{
|
||||
if (ei->hint_femp.eidx == EXFAT_HINT_NONE ||
|
||||
ei->hint_femp.eidx > dentry) {
|
||||
int total_entries = EXFAT_B_TO_DEN(i_size_read(&ei->vfs_inode));
|
||||
|
||||
if (candi_empty->count == 0) {
|
||||
candi_empty->cur = *clu;
|
||||
candi_empty->eidx = dentry;
|
||||
}
|
||||
|
||||
if (entry_type == TYPE_UNUSED)
|
||||
candi_empty->count += total_entries - dentry;
|
||||
else
|
||||
candi_empty->count++;
|
||||
|
||||
if (candi_empty->count == num_entries ||
|
||||
candi_empty->count + candi_empty->eidx == total_entries)
|
||||
ei->hint_femp = *candi_empty;
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
@ -928,17 +991,21 @@ enum {
|
||||
*/
|
||||
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, struct exfat_hint *hint_opt)
|
||||
struct exfat_hint *hint_opt)
|
||||
{
|
||||
int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len;
|
||||
int order, step, name_len = 0;
|
||||
int dentries_per_clu, num_empty = 0;
|
||||
int dentries_per_clu;
|
||||
unsigned int entry_type;
|
||||
unsigned short *uniname = NULL;
|
||||
struct exfat_chain clu;
|
||||
struct exfat_hint *hint_stat = &ei->hint_stat;
|
||||
struct exfat_hint_femp candi_empty;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
int num_entries = exfat_calc_num_entries(p_uniname);
|
||||
|
||||
if (num_entries < 0)
|
||||
return num_entries;
|
||||
|
||||
dentries_per_clu = sbi->dentries_per_clu;
|
||||
|
||||
@ -950,10 +1017,13 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
|
||||
end_eidx = dentry;
|
||||
}
|
||||
|
||||
candi_empty.eidx = EXFAT_HINT_NONE;
|
||||
exfat_reset_empty_hint(&ei->hint_femp);
|
||||
|
||||
rewind:
|
||||
order = 0;
|
||||
step = DIRENT_STEP_FILE;
|
||||
exfat_reset_empty_hint(&candi_empty);
|
||||
|
||||
while (clu.dir != EXFAT_EOF_CLUSTER) {
|
||||
i = dentry & (dentries_per_clu - 1);
|
||||
for (; i < dentries_per_clu; i++, dentry++) {
|
||||
@ -973,26 +1043,9 @@ rewind:
|
||||
entry_type == TYPE_DELETED) {
|
||||
step = DIRENT_STEP_FILE;
|
||||
|
||||
num_empty++;
|
||||
if (candi_empty.eidx == EXFAT_HINT_NONE &&
|
||||
num_empty == 1) {
|
||||
exfat_chain_set(&candi_empty.cur,
|
||||
clu.dir, clu.size, clu.flags);
|
||||
}
|
||||
|
||||
if (candi_empty.eidx == EXFAT_HINT_NONE &&
|
||||
num_empty >= num_entries) {
|
||||
candi_empty.eidx =
|
||||
dentry - (num_empty - 1);
|
||||
WARN_ON(candi_empty.eidx < 0);
|
||||
candi_empty.count = num_empty;
|
||||
|
||||
if (ei->hint_femp.eidx ==
|
||||
EXFAT_HINT_NONE ||
|
||||
candi_empty.eidx <=
|
||||
ei->hint_femp.eidx)
|
||||
ei->hint_femp = candi_empty;
|
||||
}
|
||||
exfat_set_empty_hint(ei, &candi_empty, &clu,
|
||||
dentry, num_entries,
|
||||
entry_type);
|
||||
|
||||
brelse(bh);
|
||||
if (entry_type == TYPE_UNUSED)
|
||||
@ -1000,17 +1053,14 @@ rewind:
|
||||
continue;
|
||||
}
|
||||
|
||||
num_empty = 0;
|
||||
candi_empty.eidx = EXFAT_HINT_NONE;
|
||||
exfat_reset_empty_hint(&candi_empty);
|
||||
|
||||
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;
|
||||
}
|
||||
brelse(bh);
|
||||
continue;
|
||||
}
|
||||
@ -1041,7 +1091,8 @@ rewind:
|
||||
if (entry_type == TYPE_EXTEND) {
|
||||
unsigned short entry_uniname[16], unichar;
|
||||
|
||||
if (step != DIRENT_STEP_NAME) {
|
||||
if (step != DIRENT_STEP_NAME ||
|
||||
name_len >= MAX_NAME_LENGTH) {
|
||||
step = DIRENT_STEP_FILE;
|
||||
continue;
|
||||
}
|
||||
@ -1101,12 +1152,19 @@ not_found:
|
||||
rewind = 1;
|
||||
dentry = 0;
|
||||
clu.dir = p_dir->dir;
|
||||
/* reset empty hint */
|
||||
num_empty = 0;
|
||||
candi_empty.eidx = EXFAT_HINT_NONE;
|
||||
goto rewind;
|
||||
}
|
||||
|
||||
/*
|
||||
* set the EXFAT_EOF_CLUSTER flag to avoid search
|
||||
* from the beginning again when allocated a new cluster
|
||||
*/
|
||||
if (ei->hint_femp.eidx == EXFAT_HINT_NONE) {
|
||||
ei->hint_femp.cur.dir = EXFAT_EOF_CLUSTER;
|
||||
ei->hint_femp.eidx = p_dir->size * dentries_per_clu;
|
||||
ei->hint_femp.count = 0;
|
||||
}
|
||||
|
||||
/* initialized hint_stat */
|
||||
hint_stat->clu = p_dir->dir;
|
||||
hint_stat->eidx = 0;
|
||||
@ -1154,10 +1212,8 @@ int exfat_count_ext_entries(struct super_block *sb, struct exfat_chain *p_dir,
|
||||
|
||||
type = exfat_get_entry_type(ext_ep);
|
||||
brelse(bh);
|
||||
if (type == TYPE_EXTEND || type == TYPE_STREAM)
|
||||
if (type & TYPE_CRITICAL_SEC || type & TYPE_BENIGN_SEC)
|
||||
count++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
@ -10,6 +10,11 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
#ifndef SECTOR_SIZE
|
||||
#define SECTOR_SIZE 512
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
|
||||
#include <linux/magic.h>
|
||||
@ -17,7 +22,7 @@
|
||||
#define EXFAT_SUPER_MAGIC 0x2011BAB0UL
|
||||
#endif
|
||||
|
||||
#define EXFAT_VERSION "5.19.1"
|
||||
#define EXFAT_VERSION "6.0.0"
|
||||
|
||||
#define EXFAT_ROOT_INO 1
|
||||
|
||||
@ -36,9 +41,9 @@ enum exfat_error_mode {
|
||||
* exfat nls lossy flag
|
||||
*/
|
||||
enum {
|
||||
NLS_NAME_NO_LOSSY, /* no lossy */
|
||||
NLS_NAME_LOSSY, /* just detected incorrect filename(s) */
|
||||
NLS_NAME_OVERLEN, /* the length is over than its limit */
|
||||
NLS_NAME_NO_LOSSY = 0, /* no lossy */
|
||||
NLS_NAME_LOSSY = 1 << 0, /* just detected incorrect filename(s) */
|
||||
NLS_NAME_OVERLEN = 1 << 1, /* the length is over than its limit */
|
||||
};
|
||||
|
||||
#define EXFAT_HASH_BITS 8
|
||||
@ -50,7 +55,15 @@ enum {
|
||||
#define ES_2_ENTRIES 2
|
||||
#define ES_ALL_ENTRIES 0
|
||||
|
||||
#define DIR_DELETED 0xFFFF0321
|
||||
#define ES_IDX_FILE 0
|
||||
#define ES_IDX_STREAM 1
|
||||
#define ES_IDX_FIRST_FILENAME 2
|
||||
#define EXFAT_FILENAME_ENTRY_NUM(name_len) \
|
||||
DIV_ROUND_UP(name_len, EXFAT_FILE_NAME_LEN)
|
||||
#define ES_IDX_LAST_FILENAME(name_len) \
|
||||
(ES_IDX_FIRST_FILENAME + EXFAT_FILENAME_ENTRY_NUM(name_len) - 1)
|
||||
|
||||
#define DIR_DELETED 0xFFFFFFF7
|
||||
|
||||
/* type values */
|
||||
#define TYPE_UNUSED 0x0000
|
||||
@ -71,15 +84,13 @@ enum {
|
||||
#define TYPE_PADDING 0x0402
|
||||
#define TYPE_ACLTAB 0x0403
|
||||
#define TYPE_BENIGN_SEC 0x0800
|
||||
#define TYPE_ALL 0x0FFF
|
||||
#define TYPE_VENDOR_EXT 0x0801
|
||||
#define TYPE_VENDOR_ALLOC 0x0802
|
||||
|
||||
#define MAX_CHARSET_SIZE 6 /* max size of multi-byte character */
|
||||
#define MAX_NAME_LENGTH 255 /* max len of file name excluding NULL */
|
||||
#define MAX_VFSNAME_BUF_SIZE ((MAX_NAME_LENGTH + 1) * MAX_CHARSET_SIZE)
|
||||
|
||||
/* Enough size to hold 256 dentry (even 512 Byte sector) */
|
||||
#define DIR_CACHE_SIZE (256*sizeof(struct exfat_dentry)/512+1)
|
||||
|
||||
#define EXFAT_HINT_NONE -1
|
||||
#define EXFAT_MIN_SUBDIR 2
|
||||
|
||||
@ -104,11 +115,17 @@ enum {
|
||||
/*
|
||||
* helpers for block size to dentry size conversion.
|
||||
*/
|
||||
#define EXFAT_B_TO_DEN_IDX(b, sbi) \
|
||||
((b) << ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS))
|
||||
#define EXFAT_B_TO_DEN(b) ((b) >> DENTRY_SIZE_BITS)
|
||||
#define EXFAT_DEN_TO_B(b) ((b) << DENTRY_SIZE_BITS)
|
||||
|
||||
/*
|
||||
* helpers for cluster size to dentry size conversion.
|
||||
*/
|
||||
#define EXFAT_CLU_TO_DEN(clu, sbi) \
|
||||
((clu) << ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS))
|
||||
#define EXFAT_DEN_TO_CLU(dentry, sbi) \
|
||||
((dentry) >> ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS))
|
||||
|
||||
/*
|
||||
* helpers for fat entry.
|
||||
*/
|
||||
@ -134,6 +151,17 @@ enum {
|
||||
#define BITS_PER_BYTE_MASK 0x7
|
||||
#define IGNORED_BITS_REMAINED(clu, clu_base) ((1 << ((clu) - (clu_base))) - 1)
|
||||
|
||||
#define ES_ENTRY_NUM(name_len) (ES_IDX_LAST_FILENAME(name_len) + 1)
|
||||
/* 19 entries = 1 file entry + 1 stream entry + 17 filename entries */
|
||||
#define ES_MAX_ENTRY_NUM ES_ENTRY_NUM(MAX_NAME_LENGTH)
|
||||
|
||||
/*
|
||||
* 19 entries x 32 bytes/entry = 608 bytes.
|
||||
* The 608 bytes are in 3 sectors at most (even 512 Byte sector).
|
||||
*/
|
||||
#define DIR_CACHE_SIZE \
|
||||
(DIV_ROUND_UP(EXFAT_DEN_TO_B(ES_MAX_ENTRY_NUM), SECTOR_SIZE) + 1)
|
||||
|
||||
struct exfat_dentry_namebuf {
|
||||
char *lfn;
|
||||
int lfnbuf_len; /* usually MAX_UNINAME_BUF_SIZE */
|
||||
@ -175,13 +203,16 @@ struct exfat_hint {
|
||||
|
||||
struct exfat_entry_set_cache {
|
||||
struct super_block *sb;
|
||||
bool modified;
|
||||
unsigned int start_off;
|
||||
int num_bh;
|
||||
struct buffer_head *bh[DIR_CACHE_SIZE];
|
||||
struct buffer_head *__bh[DIR_CACHE_SIZE];
|
||||
struct buffer_head **bh;
|
||||
unsigned int num_entries;
|
||||
bool modified;
|
||||
};
|
||||
|
||||
#define IS_DYNAMIC_ES(es) ((es)->__bh != (es)->bh)
|
||||
|
||||
struct exfat_dir_entry {
|
||||
struct exfat_chain dir;
|
||||
int entry;
|
||||
@ -394,7 +425,7 @@ static inline sector_t exfat_cluster_to_sector(struct exfat_sb_info *sbi,
|
||||
sbi->data_start_sector;
|
||||
}
|
||||
|
||||
static inline int exfat_sector_to_cluster(struct exfat_sb_info *sbi,
|
||||
static inline unsigned int exfat_sector_to_cluster(struct exfat_sb_info *sbi,
|
||||
sector_t sec)
|
||||
{
|
||||
return ((sec - sbi->data_start_sector) >> sbi->sect_per_clus_bits) +
|
||||
@ -442,15 +473,23 @@ 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);
|
||||
int __exfat_truncate(struct inode *inode);
|
||||
void exfat_truncate(struct inode *inode);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
struct iattr *attr);
|
||||
int exfat_getattr(struct mnt_idmap *idmap, const struct path *path,
|
||||
struct kstat *stat, unsigned int request_mask,
|
||||
unsigned int query_flags);
|
||||
#else
|
||||
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);
|
||||
#endif
|
||||
#else
|
||||
int exfat_setattr(struct dentry *dentry, struct iattr *attr);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
@ -496,15 +535,16 @@ 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, struct exfat_hint *hint_opt);
|
||||
struct exfat_hint *hint_opt);
|
||||
int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu);
|
||||
struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
|
||||
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,
|
||||
struct exfat_chain *p_dir, int entry, unsigned int type);
|
||||
int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync);
|
||||
int exfat_get_dentry_set(struct exfat_entry_set_cache *es,
|
||||
struct super_block *sb, struct exfat_chain *p_dir, int entry,
|
||||
unsigned int type);
|
||||
int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync);
|
||||
int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir);
|
||||
|
||||
/* inode.c */
|
||||
@ -515,6 +555,7 @@ struct inode *exfat_build_inode(struct super_block *sb,
|
||||
void exfat_hash_inode(struct inode *inode, loff_t i_pos);
|
||||
void exfat_unhash_inode(struct inode *inode);
|
||||
struct inode *exfat_iget(struct super_block *sb, loff_t i_pos);
|
||||
int __exfat_write_inode(struct inode *inode, int sync);
|
||||
int exfat_write_inode(struct inode *inode, struct writeback_control *wbc);
|
||||
void exfat_evict_inode(struct inode *inode);
|
||||
int exfat_block_truncate_page(struct inode *inode, loff_t from);
|
||||
@ -540,14 +581,16 @@ void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
|
||||
#define exfat_fs_error_ratelimit(sb, fmt, args...) \
|
||||
__exfat_fs_error(sb, __ratelimit(&EXFAT_SB(sb)->ratelimit), \
|
||||
fmt, ## args)
|
||||
void exfat_msg(struct super_block *sb, const char *lv, const char *fmt, ...)
|
||||
__printf(3, 4) __cold;
|
||||
|
||||
/* expand to pr_*() with prefix */
|
||||
#define exfat_err(sb, fmt, ...) \
|
||||
exfat_msg(sb, KERN_ERR, fmt, ##__VA_ARGS__)
|
||||
pr_err("exFAT-fs (%s): " fmt "\n", (sb)->s_id, ##__VA_ARGS__)
|
||||
#define exfat_warn(sb, fmt, ...) \
|
||||
exfat_msg(sb, KERN_WARNING, fmt, ##__VA_ARGS__)
|
||||
pr_warn("exFAT-fs (%s): " fmt "\n", (sb)->s_id, ##__VA_ARGS__)
|
||||
#define exfat_info(sb, fmt, ...) \
|
||||
exfat_msg(sb, KERN_INFO, fmt, ##__VA_ARGS__)
|
||||
pr_info("exFAT-fs (%s): " fmt "\n", (sb)->s_id, ##__VA_ARGS__)
|
||||
#define exfat_debug(sb, fmt, ...) \
|
||||
pr_debug("exFAT-fs (%s): " fmt "\n", (sb)->s_id, ##__VA_ARGS__)
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
||||
void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
|
||||
|
@ -27,6 +27,7 @@
|
||||
((sbi)->num_clusters - EXFAT_RESERVED_CLUSTERS)
|
||||
|
||||
/* AllocationPossible and NoFatChain field in GeneralSecondaryFlags Field */
|
||||
#define ALLOC_POSSIBLE 0x01
|
||||
#define ALLOC_FAT_CHAIN 0x01
|
||||
#define ALLOC_NO_FAT_CHAIN 0x03
|
||||
|
||||
@ -50,6 +51,8 @@
|
||||
#define EXFAT_STREAM 0xC0 /* stream entry */
|
||||
#define EXFAT_NAME 0xC1 /* file name entry */
|
||||
#define EXFAT_ACL 0xC2 /* stream entry */
|
||||
#define EXFAT_VENDOR_EXT 0xE0 /* vendor extension entry */
|
||||
#define EXFAT_VENDOR_ALLOC 0xE1 /* vendor allocation entry */
|
||||
|
||||
#define IS_EXFAT_CRITICAL_PRI(x) (x < 0xA0)
|
||||
#define IS_EXFAT_BENIGN_PRI(x) (x < 0xC0)
|
||||
@ -155,6 +158,24 @@ struct exfat_dentry {
|
||||
__le32 start_clu;
|
||||
__le64 size;
|
||||
} __packed upcase; /* up-case table directory entry */
|
||||
struct {
|
||||
__u8 flags;
|
||||
__u8 vendor_guid[16];
|
||||
__u8 vendor_defined[14];
|
||||
} __packed vendor_ext; /* vendor extension directory entry */
|
||||
struct {
|
||||
__u8 flags;
|
||||
__u8 vendor_guid[16];
|
||||
__u8 vendor_defined[2];
|
||||
__le32 start_clu;
|
||||
__le64 size;
|
||||
} __packed vendor_alloc; /* vendor allocation directory entry */
|
||||
struct {
|
||||
__u8 flags;
|
||||
__u8 custom_defined[18];
|
||||
__le32 start_clu;
|
||||
__le64 size;
|
||||
} __packed generic_secondary; /* generic secondary directory entry */
|
||||
} __packed dentry;
|
||||
} __packed;
|
||||
|
||||
|
@ -278,8 +278,7 @@ 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 *bh;
|
||||
sector_t blknr, last_blknr;
|
||||
int i;
|
||||
sector_t blknr, last_blknr, i;
|
||||
|
||||
blknr = exfat_cluster_to_sector(sbi, clu);
|
||||
last_blknr = blknr + sbi->sect_per_clus;
|
||||
@ -323,7 +322,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
||||
struct exfat_chain *p_chain, bool sync_bmap)
|
||||
{
|
||||
int ret = -ENOSPC;
|
||||
unsigned int num_clusters = 0, total_cnt;
|
||||
unsigned int total_cnt;
|
||||
unsigned int hint_clu, new_clu, last_clu = EXFAT_EOF_CLUSTER;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
@ -346,7 +345,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
||||
/* find new cluster */
|
||||
if (hint_clu == EXFAT_EOF_CLUSTER) {
|
||||
if (sbi->clu_srch_ptr < EXFAT_FIRST_CLUSTER) {
|
||||
exfat_err(sb, "sbi->clu_srch_ptr is invalid (%u)\n",
|
||||
exfat_err(sb, "sbi->clu_srch_ptr is invalid (%u)",
|
||||
sbi->clu_srch_ptr);
|
||||
sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER;
|
||||
}
|
||||
@ -360,18 +359,12 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
||||
|
||||
/* check cluster validation */
|
||||
if (!is_valid_cluster(sbi, hint_clu)) {
|
||||
exfat_err(sb, "hint_cluster is invalid (%u)",
|
||||
if (hint_clu != sbi->num_clusters)
|
||||
exfat_err(sb, "hint_cluster is invalid (%u), rewind to the first cluster",
|
||||
hint_clu);
|
||||
hint_clu = EXFAT_FIRST_CLUSTER;
|
||||
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
|
||||
if (exfat_chain_cont_cluster(sb, p_chain->dir,
|
||||
num_clusters)) {
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
p_chain->flags = ALLOC_FAT_CHAIN;
|
||||
}
|
||||
}
|
||||
|
||||
p_chain->dir = EXFAT_EOF_CLUSTER;
|
||||
|
||||
@ -380,7 +373,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
||||
if (new_clu != hint_clu &&
|
||||
p_chain->flags == ALLOC_NO_FAT_CHAIN) {
|
||||
if (exfat_chain_cont_cluster(sb, p_chain->dir,
|
||||
num_clusters)) {
|
||||
p_chain->size)) {
|
||||
ret = -EIO;
|
||||
goto free_cluster;
|
||||
}
|
||||
@ -393,8 +386,6 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
||||
goto free_cluster;
|
||||
}
|
||||
|
||||
num_clusters++;
|
||||
|
||||
/* update FAT table */
|
||||
if (p_chain->flags == ALLOC_FAT_CHAIN) {
|
||||
if (exfat_ent_set(sb, new_clu, EXFAT_EOF_CLUSTER)) {
|
||||
@ -411,13 +402,14 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
||||
goto free_cluster;
|
||||
}
|
||||
}
|
||||
p_chain->size++;
|
||||
|
||||
last_clu = new_clu;
|
||||
|
||||
if (--num_alloc == 0) {
|
||||
if (p_chain->size == num_alloc) {
|
||||
sbi->clu_srch_ptr = hint_clu;
|
||||
sbi->used_clusters += num_clusters;
|
||||
sbi->used_clusters += num_alloc;
|
||||
|
||||
p_chain->size += num_clusters;
|
||||
mutex_unlock(&sbi->bitmap_lock);
|
||||
return 0;
|
||||
}
|
||||
@ -428,7 +420,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
||||
|
||||
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
|
||||
if (exfat_chain_cont_cluster(sb, p_chain->dir,
|
||||
num_clusters)) {
|
||||
p_chain->size)) {
|
||||
ret = -EIO;
|
||||
goto free_cluster;
|
||||
}
|
||||
@ -437,7 +429,6 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
||||
}
|
||||
}
|
||||
free_cluster:
|
||||
if (num_clusters)
|
||||
__exfat_free_cluster(inode, p_chain);
|
||||
unlock:
|
||||
mutex_unlock(&sbi->bitmap_lock);
|
||||
|
155
fs/exfat/file.c
155
fs/exfat/file.c
@ -100,7 +100,7 @@ static int exfat_sanitize_mode(const struct exfat_sb_info *sbi,
|
||||
}
|
||||
|
||||
/* resize the file length */
|
||||
int __exfat_truncate(struct inode *inode, loff_t new_size)
|
||||
int __exfat_truncate(struct inode *inode)
|
||||
{
|
||||
unsigned int num_clusters_new, num_clusters_phys;
|
||||
unsigned int last_clu = EXFAT_FREE_CLUSTER;
|
||||
@ -108,7 +108,6 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
int evict = (ei->dir.dir == DIR_DELETED) ? 1 : 0;
|
||||
|
||||
/* check if the given file ID is opened */
|
||||
if (ei->type != TYPE_FILE && ei->type != TYPE_DIR)
|
||||
@ -121,7 +120,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
|
||||
|
||||
exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags);
|
||||
|
||||
if (new_size > 0) {
|
||||
if (i_size_read(inode) > 0) {
|
||||
/*
|
||||
* Truncate FAT chain num_clusters after the first cluster
|
||||
* num_clusters = min(new, phys);
|
||||
@ -151,63 +150,22 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
|
||||
ei->start_clu = EXFAT_EOF_CLUSTER;
|
||||
}
|
||||
|
||||
i_size_write(inode, new_size);
|
||||
|
||||
if (ei->type == TYPE_FILE)
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
|
||||
/* 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;
|
||||
|
||||
es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry,
|
||||
ES_ALL_ENTRIES);
|
||||
if (!es)
|
||||
/*
|
||||
* update the directory entry
|
||||
*
|
||||
* If the directory entry is updated by mark_inode_dirty(), the
|
||||
* directory entry will be written after a writeback cycle of
|
||||
* updating the bitmap/FAT, which may result in clusters being
|
||||
* freed but referenced by the directory entry in the event of a
|
||||
* sudden power failure.
|
||||
* __exfat_write_inode() is called for directory entry, bitmap
|
||||
* and FAT to be written in a same writeback.
|
||||
*/
|
||||
if (__exfat_write_inode(inode, inode_needs_sync(inode)))
|
||||
return -EIO;
|
||||
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,
|
||||
&ep->dentry.file.modify_date,
|
||||
&ep->dentry.file.modify_time_cs);
|
||||
ep->dentry.file.attr = cpu_to_le16(ei->attr);
|
||||
|
||||
/* File size should be zero if there is no cluster allocated */
|
||||
if (ei->start_clu == EXFAT_EOF_CLUSTER) {
|
||||
ep2->dentry.stream.valid_size = 0;
|
||||
ep2->dentry.stream.size = 0;
|
||||
} else {
|
||||
ep2->dentry.stream.valid_size = cpu_to_le64(new_size);
|
||||
ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
|
||||
}
|
||||
|
||||
if (new_size == 0) {
|
||||
/* Any directory can not be truncated to zero */
|
||||
WARN_ON(ei->type != TYPE_FILE);
|
||||
|
||||
ep2->dentry.stream.flags = ALLOC_FAT_CHAIN;
|
||||
ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER;
|
||||
}
|
||||
|
||||
exfat_update_dir_chksum_with_entry_set(es);
|
||||
err = exfat_free_dentry_set(es, inode_needs_sync(inode));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* cut off from the FAT chain */
|
||||
if (ei->flags == ALLOC_FAT_CHAIN && last_clu != EXFAT_FREE_CLUSTER &&
|
||||
@ -236,7 +194,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void exfat_truncate(struct inode *inode, loff_t size)
|
||||
void exfat_truncate(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
@ -258,22 +216,11 @@ void exfat_truncate(struct inode *inode, loff_t size)
|
||||
goto write_size;
|
||||
}
|
||||
|
||||
err = __exfat_truncate(inode, i_size_read(inode));
|
||||
err = __exfat_truncate(inode);
|
||||
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)) &
|
||||
~((loff_t)sbi->cluster_size - 1)) >> inode->i_blkbits;
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
|
||||
write_size:
|
||||
aligned_size = i_size_read(inode);
|
||||
if (aligned_size & (blocksize - 1)) {
|
||||
@ -290,9 +237,15 @@ write_size:
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
int exfat_getattr(struct mnt_idmap *idmap, const struct path *path,
|
||||
struct kstat *stat, unsigned int request_mask,
|
||||
unsigned int query_flags)
|
||||
#else
|
||||
int exfat_getattr(struct user_namespace *mnt_uerns, const struct path *path,
|
||||
struct kstat *stat, unsigned int request_mask,
|
||||
unsigned int query_flags)
|
||||
#endif
|
||||
#else
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
int exfat_getattr(const struct path *path, struct kstat *stat,
|
||||
@ -311,7 +264,11 @@ int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
generic_fillattr(&nop_mnt_idmap, inode, stat);
|
||||
#else
|
||||
generic_fillattr(&init_user_ns, inode, stat);
|
||||
#endif
|
||||
#else
|
||||
generic_fillattr(inode, stat);
|
||||
#endif
|
||||
@ -326,8 +283,13 @@ int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
struct iattr *attr)
|
||||
#else
|
||||
int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||
struct iattr *attr)
|
||||
#endif
|
||||
#else
|
||||
int exfat_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
#endif
|
||||
@ -357,7 +319,11 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
(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)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
error = setattr_prepare(&nop_mnt_idmap, dentry, attr);
|
||||
#else
|
||||
error = setattr_prepare(&init_user_ns, dentry, attr);
|
||||
#endif
|
||||
#else
|
||||
error = setattr_prepare(dentry, attr);
|
||||
#endif
|
||||
@ -387,6 +353,24 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
attr->ia_valid &= ~ATTR_MODE;
|
||||
}
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE)
|
||||
#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
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
setattr_copy(&nop_mnt_idmap, inode, attr);
|
||||
#else
|
||||
setattr_copy(&init_user_ns, inode, attr);
|
||||
#endif
|
||||
#else
|
||||
setattr_copy(inode, attr);
|
||||
#endif
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE) {
|
||||
error = exfat_block_truncate_page(inode, attr->ia_size);
|
||||
if (error)
|
||||
@ -394,39 +378,46 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
|
||||
down_write(&EXFAT_I(inode)->truncate_lock);
|
||||
truncate_setsize(inode, attr->ia_size);
|
||||
exfat_truncate(inode, attr->ia_size);
|
||||
/*
|
||||
* __exfat_write_inode() is called from exfat_truncate(), inode
|
||||
* is already written by it, so mark_inode_dirty() is unneeded.
|
||||
*/
|
||||
exfat_truncate(inode);
|
||||
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);
|
||||
} else
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0)
|
||||
struct request_queue *q = bdev_get_queue(inode->i_sb->s_bdev);
|
||||
#endif
|
||||
struct fstrim_range range;
|
||||
int ret = 0;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
if (!bdev_max_discard_sectors(inode->i_sb->s_bdev))
|
||||
#else
|
||||
if (!blk_queue_discard(q))
|
||||
#endif
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (copy_from_user(&range, (struct fstrim_range __user *)arg, sizeof(range)))
|
||||
return -EFAULT;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
range.minlen = max_t(unsigned int, range.minlen,
|
||||
bdev_discard_granularity(inode->i_sb->s_bdev));
|
||||
#else
|
||||
range.minlen = max_t(unsigned int, range.minlen,
|
||||
q->limits.discard_granularity);
|
||||
#endif
|
||||
|
||||
ret = exfat_trim_fs(inode, &range);
|
||||
if (ret < 0)
|
||||
@ -492,7 +483,11 @@ const struct file_operations exfat_file_operations = {
|
||||
#endif
|
||||
.mmap = generic_file_mmap,
|
||||
.fsync = exfat_file_fsync,
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
|
||||
.splice_read = filemap_splice_read,
|
||||
#else
|
||||
.splice_read = generic_file_splice_read,
|
||||
#endif
|
||||
.splice_write = iter_file_splice_write,
|
||||
};
|
||||
|
||||
|
105
fs/exfat/inode.c
105
fs/exfat/inode.c
@ -19,11 +19,11 @@
|
||||
#include "exfat_raw.h"
|
||||
#include "exfat_fs.h"
|
||||
|
||||
static int __exfat_write_inode(struct inode *inode, int sync)
|
||||
int __exfat_write_inode(struct inode *inode, int sync)
|
||||
{
|
||||
unsigned long long on_disk_size;
|
||||
struct exfat_dentry *ep, *ep2;
|
||||
struct exfat_entry_set_cache *es = NULL;
|
||||
struct exfat_entry_set_cache es;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
@ -44,11 +44,10 @@ static int __exfat_write_inode(struct inode *inode, int sync)
|
||||
exfat_set_volume_dirty(sb);
|
||||
|
||||
/* get the directory entry of given file or directory */
|
||||
es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES);
|
||||
if (!es)
|
||||
if (exfat_get_dentry_set(&es, sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES))
|
||||
return -EIO;
|
||||
ep = exfat_get_dentry_cached(es, 0);
|
||||
ep2 = exfat_get_dentry_cached(es, 1);
|
||||
ep = exfat_get_dentry_cached(&es, ES_IDX_FILE);
|
||||
ep2 = exfat_get_dentry_cached(&es, ES_IDX_STREAM);
|
||||
|
||||
ep->dentry.file.attr = cpu_to_le16(exfat_make_attr(inode));
|
||||
|
||||
@ -77,9 +76,16 @@ static int __exfat_write_inode(struct inode *inode, int sync)
|
||||
|
||||
ep2->dentry.stream.valid_size = cpu_to_le64(on_disk_size);
|
||||
ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
|
||||
if (on_disk_size) {
|
||||
ep2->dentry.stream.flags = ei->flags;
|
||||
ep2->dentry.stream.start_clu = cpu_to_le32(ei->start_clu);
|
||||
} else {
|
||||
ep2->dentry.stream.flags = ALLOC_FAT_CHAIN;
|
||||
ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER;
|
||||
}
|
||||
|
||||
exfat_update_dir_chksum_with_entry_set(es);
|
||||
return exfat_free_dentry_set(es, sync);
|
||||
exfat_update_dir_chksum_with_entry_set(&es);
|
||||
return exfat_put_dentry_set(&es, sync);
|
||||
}
|
||||
|
||||
int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
@ -107,7 +113,7 @@ void exfat_sync_inode(struct inode *inode)
|
||||
static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
|
||||
unsigned int *clu, int create)
|
||||
{
|
||||
int ret, modified = false;
|
||||
int ret;
|
||||
unsigned int last_clu;
|
||||
struct exfat_chain new_clu;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
@ -198,7 +204,6 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
|
||||
if (new_clu.flags == ALLOC_FAT_CHAIN)
|
||||
ei->flags = ALLOC_FAT_CHAIN;
|
||||
ei->start_clu = new_clu.dir;
|
||||
modified = true;
|
||||
} else {
|
||||
if (new_clu.flags != ei->flags) {
|
||||
/* no-fat-chain bit is disabled,
|
||||
@ -208,7 +213,6 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
|
||||
exfat_chain_cont_cluster(sb, ei->start_clu,
|
||||
num_clusters);
|
||||
ei->flags = ALLOC_FAT_CHAIN;
|
||||
modified = true;
|
||||
}
|
||||
if (new_clu.flags == ALLOC_FAT_CHAIN)
|
||||
if (exfat_ent_set(sb, last_clu, new_clu.dir))
|
||||
@ -218,35 +222,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
|
||||
num_clusters += num_to_be_allocated;
|
||||
*clu = new_clu.dir;
|
||||
|
||||
if (ei->dir.dir != DIR_DELETED && modified) {
|
||||
struct exfat_dentry *ep;
|
||||
struct exfat_entry_set_cache *es;
|
||||
int err;
|
||||
|
||||
es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry,
|
||||
ES_ALL_ENTRIES);
|
||||
if (!es)
|
||||
return -EIO;
|
||||
/* get stream entry */
|
||||
ep = exfat_get_dentry_cached(es, 1);
|
||||
|
||||
/* update directory entry */
|
||||
ep->dentry.stream.flags = ei->flags;
|
||||
ep->dentry.stream.start_clu =
|
||||
cpu_to_le32(ei->start_clu);
|
||||
ep->dentry.stream.valid_size =
|
||||
cpu_to_le64(i_size_read(inode));
|
||||
ep->dentry.stream.size =
|
||||
ep->dentry.stream.valid_size;
|
||||
|
||||
exfat_update_dir_chksum_with_entry_set(es);
|
||||
err = exfat_free_dentry_set(es, inode_needs_sync(inode));
|
||||
if (err)
|
||||
return err;
|
||||
} /* end of if != DIR_DELETED */
|
||||
|
||||
inode->i_blocks +=
|
||||
num_to_be_allocated << sbi->sect_per_clus_bits;
|
||||
inode->i_blocks += EXFAT_CLU_TO_B(num_to_be_allocated, sbi) >> 9;
|
||||
|
||||
/*
|
||||
* Move *clu pointer along FAT chains (hole care) because the
|
||||
@ -359,10 +335,17 @@ unlock_ret:
|
||||
return err;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
static int exfat_read_folio(struct file *file, struct folio *folio)
|
||||
{
|
||||
return mpage_read_folio(folio, exfat_get_block);
|
||||
}
|
||||
#else
|
||||
static int exfat_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
return mpage_readpage(page, exfat_get_block);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||
static void exfat_readahead(struct readahead_control *rac)
|
||||
@ -377,10 +360,12 @@ static int exfat_readpages(struct file *file, struct address_space *mapping,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)
|
||||
static int exfat_writepage(struct page *page, struct writeback_control *wbc)
|
||||
{
|
||||
return block_write_full_page(page, exfat_get_block, wbc);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int exfat_writepages(struct address_space *mapping,
|
||||
struct writeback_control *wbc)
|
||||
@ -394,21 +379,37 @@ static void exfat_write_failed(struct address_space *mapping, loff_t to)
|
||||
|
||||
if (to > i_size_read(inode)) {
|
||||
truncate_pagecache(inode, i_size_read(inode));
|
||||
exfat_truncate(inode, EXFAT_I(inode)->i_size_aligned);
|
||||
#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
|
||||
exfat_truncate(inode);
|
||||
}
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
static int exfat_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned int len,
|
||||
struct page **pagep, void **fsdata)
|
||||
#else
|
||||
static int exfat_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned int len, unsigned int flags,
|
||||
struct page **pagep, void **fsdata)
|
||||
#endif
|
||||
{
|
||||
int ret;
|
||||
|
||||
*pagep = NULL;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
ret = cont_write_begin(file, mapping, pos, len, pagep, fsdata,
|
||||
exfat_get_block,
|
||||
&EXFAT_I(mapping->host)->i_size_ondisk);
|
||||
#else
|
||||
ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
|
||||
exfat_get_block,
|
||||
&EXFAT_I(mapping->host)->i_size_ondisk);
|
||||
|
||||
#endif
|
||||
if (ret < 0)
|
||||
exfat_write_failed(mapping, pos+len);
|
||||
|
||||
@ -522,18 +523,29 @@ static const struct address_space_operations exfat_aops = {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
|
||||
.invalidate_folio = block_invalidate_folio,
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
.read_folio = exfat_read_folio,
|
||||
#else
|
||||
.readpage = exfat_readpage,
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||
.readahead = exfat_readahead,
|
||||
#else
|
||||
.readpages = exfat_readpages,
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)
|
||||
.writepage = exfat_writepage,
|
||||
#endif
|
||||
.writepages = exfat_writepages,
|
||||
.write_begin = exfat_write_begin,
|
||||
.write_end = exfat_write_end,
|
||||
.direct_IO = exfat_direct_IO,
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)
|
||||
.bmap = exfat_aop_bmap
|
||||
#else
|
||||
.bmap = exfat_aop_bmap,
|
||||
.migrate_folio = buffer_migrate_folio,
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline unsigned long exfat_hash(loff_t i_pos)
|
||||
@ -611,7 +623,11 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
|
||||
#else
|
||||
inode->i_version++;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||
inode->i_generation = get_random_u32();
|
||||
#else
|
||||
inode->i_generation = prandom_u32();
|
||||
#endif
|
||||
|
||||
if (info->attr & ATTR_SUBDIR) { /* directory */
|
||||
inode->i_generation &= ~1;
|
||||
@ -641,8 +657,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)) &
|
||||
~((loff_t)sbi->cluster_size - 1)) >> inode->i_blkbits;
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
|
||||
inode->i_mtime = info->mtime;
|
||||
inode->i_ctime = info->mtime;
|
||||
ei->i_crtime = info->crtime;
|
||||
@ -690,7 +705,7 @@ void exfat_evict_inode(struct inode *inode)
|
||||
if (!inode->i_nlink) {
|
||||
i_size_write(inode, 0);
|
||||
mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
|
||||
__exfat_truncate(inode, 0);
|
||||
__exfat_truncate(inode);
|
||||
mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock);
|
||||
}
|
||||
|
||||
|
@ -52,23 +52,6 @@ void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* exfat_msg() - print preformated EXFAT specific messages.
|
||||
* All logs except what uses exfat_fs_error() should be written by exfat_msg()
|
||||
*/
|
||||
void exfat_msg(struct super_block *sb, const char *level, const char *fmt, ...)
|
||||
{
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &args;
|
||||
/* level means KERN_ pacility level */
|
||||
printk("%sexFAT-fs (%s): %pV\n", level, sb->s_id, &vaf);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#define SECS_PER_MIN (60)
|
||||
#define TIMEZONE_SEC(x) ((x) * 15 * SECS_PER_MIN)
|
||||
|
||||
|
108
fs/exfat/namei.c
108
fs/exfat/namei.c
@ -251,11 +251,18 @@ static int exfat_search_empty_slot(struct super_block *sb,
|
||||
|
||||
if (hint_femp->eidx != EXFAT_HINT_NONE) {
|
||||
dentry = hint_femp->eidx;
|
||||
if (num_entries <= hint_femp->count) {
|
||||
hint_femp->eidx = EXFAT_HINT_NONE;
|
||||
return dentry;
|
||||
}
|
||||
|
||||
/*
|
||||
* If hint_femp->count is enough, it is needed to check if
|
||||
* there are actual empty entries.
|
||||
* Otherwise, and if "dentry + hint_famp->count" is also equal
|
||||
* to "p_dir->size * dentries_per_clu", it means ENOSPC.
|
||||
*/
|
||||
if (dentry + hint_femp->count == p_dir->size * dentries_per_clu &&
|
||||
num_entries > hint_femp->count)
|
||||
return -ENOSPC;
|
||||
|
||||
hint_femp->eidx = EXFAT_HINT_NONE;
|
||||
exfat_chain_dup(&clu, &hint_femp->cur);
|
||||
} else {
|
||||
exfat_chain_dup(&clu, p_dir);
|
||||
@ -320,6 +327,12 @@ static int exfat_search_empty_slot(struct super_block *sb,
|
||||
}
|
||||
}
|
||||
|
||||
hint_femp->eidx = p_dir->size * dentries_per_clu - num_empty;
|
||||
hint_femp->count = num_empty;
|
||||
if (num_empty == 0)
|
||||
exfat_chain_set(&hint_femp->cur, EXFAT_EOF_CLUSTER, 0,
|
||||
clu.flags);
|
||||
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
@ -345,7 +358,6 @@ static int exfat_find_empty_entry(struct inode *inode,
|
||||
unsigned int ret, last_clu;
|
||||
loff_t size = 0;
|
||||
struct exfat_chain clu;
|
||||
struct exfat_dentry *ep = NULL;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
@ -397,44 +409,21 @@ static int exfat_find_empty_entry(struct inode *inode,
|
||||
if (exfat_ent_set(sb, last_clu, clu.dir))
|
||||
return -EIO;
|
||||
|
||||
if (hint_femp.eidx == EXFAT_HINT_NONE) {
|
||||
/* the special case that new dentry
|
||||
* should be allocated from the start of new cluster
|
||||
*/
|
||||
hint_femp.eidx = EXFAT_B_TO_DEN_IDX(p_dir->size, sbi);
|
||||
hint_femp.count = sbi->dentries_per_clu;
|
||||
|
||||
if (hint_femp.cur.dir == EXFAT_EOF_CLUSTER)
|
||||
exfat_chain_set(&hint_femp.cur, clu.dir, 0, clu.flags);
|
||||
}
|
||||
|
||||
hint_femp.count += sbi->dentries_per_clu;
|
||||
|
||||
hint_femp.cur.size++;
|
||||
p_dir->size++;
|
||||
size = EXFAT_CLU_TO_B(p_dir->size, sbi);
|
||||
|
||||
/* update the directory entry */
|
||||
if (p_dir->dir != sbi->root_dir) {
|
||||
struct buffer_head *bh;
|
||||
|
||||
ep = exfat_get_dentry(sb,
|
||||
&(ei->dir), ei->entry + 1, &bh);
|
||||
if (!ep)
|
||||
return -EIO;
|
||||
|
||||
ep->dentry.stream.valid_size = cpu_to_le64(size);
|
||||
ep->dentry.stream.size = ep->dentry.stream.valid_size;
|
||||
ep->dentry.stream.flags = p_dir->flags;
|
||||
exfat_update_bh(bh, IS_DIRSYNC(inode));
|
||||
brelse(bh);
|
||||
if (exfat_update_dir_chksum(inode, &(ei->dir),
|
||||
ei->entry))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* directory inode should be updated in here */
|
||||
i_size_write(inode, size);
|
||||
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;
|
||||
inode->i_blocks += sbi->cluster_size >> 9;
|
||||
}
|
||||
|
||||
return dentry;
|
||||
@ -489,7 +478,7 @@ static int __exfat_resolve_path(struct inode *inode, const unsigned char *path,
|
||||
return namelen; /* return error value */
|
||||
|
||||
if ((lossy && !lookup) || !namelen)
|
||||
return -EINVAL;
|
||||
return (lossy & NLS_NAME_OVERLEN) ? -ENAMETOOLONG : -EINVAL;
|
||||
|
||||
exfat_chain_set(p_dir, ei->start_clu,
|
||||
EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags);
|
||||
@ -590,8 +579,13 @@ out:
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
static int exfat_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, bool excl)
|
||||
#else
|
||||
static int exfat_create(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, bool excl)
|
||||
#endif
|
||||
#else
|
||||
static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
bool excl)
|
||||
@ -658,14 +652,14 @@ unlock:
|
||||
static int exfat_find(struct inode *dir, struct qstr *qname,
|
||||
struct exfat_dir_entry *info)
|
||||
{
|
||||
int ret, dentry, num_entries, count;
|
||||
int ret, dentry, count;
|
||||
struct exfat_chain cdir;
|
||||
struct exfat_uni_name uni_name;
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_inode_info *ei = EXFAT_I(dir);
|
||||
struct exfat_dentry *ep, *ep2;
|
||||
struct exfat_entry_set_cache *es;
|
||||
struct exfat_entry_set_cache es;
|
||||
/* for optimized dir & entry to prevent long traverse of cluster chain */
|
||||
struct exfat_hint hint_opt;
|
||||
|
||||
@ -677,10 +671,6 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
num_entries = exfat_calc_num_entries(&uni_name);
|
||||
if (num_entries < 0)
|
||||
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)) {
|
||||
@ -698,9 +688,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
|
||||
}
|
||||
|
||||
/* search the file name for directories */
|
||||
dentry = exfat_find_dir_entry(sb, ei, &cdir, &uni_name,
|
||||
num_entries, TYPE_ALL, &hint_opt);
|
||||
|
||||
dentry = exfat_find_dir_entry(sb, ei, &cdir, &uni_name, &hint_opt);
|
||||
if (dentry < 0)
|
||||
return dentry; /* -error value */
|
||||
|
||||
@ -713,11 +701,10 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
|
||||
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)
|
||||
if (exfat_get_dentry_set(&es, sb, &cdir, dentry, ES_2_ENTRIES))
|
||||
return -EIO;
|
||||
ep = exfat_get_dentry_cached(es, 0);
|
||||
ep2 = exfat_get_dentry_cached(es, 1);
|
||||
ep = exfat_get_dentry_cached(&es, ES_IDX_FILE);
|
||||
ep2 = exfat_get_dentry_cached(&es, ES_IDX_STREAM);
|
||||
|
||||
info->type = exfat_get_entry_type(ep);
|
||||
info->attr = le16_to_cpu(ep->dentry.file.attr);
|
||||
@ -746,7 +733,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
|
||||
ep->dentry.file.access_time,
|
||||
ep->dentry.file.access_date,
|
||||
0);
|
||||
exfat_free_dentry_set(es, false);
|
||||
exfat_put_dentry_set(&es, false);
|
||||
|
||||
if (ei->start_clu == EXFAT_FREE_CLUSTER) {
|
||||
exfat_fs_error(sb,
|
||||
@ -931,8 +918,13 @@ unlock:
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
static int exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode)
|
||||
#else
|
||||
static int exfat_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode)
|
||||
#endif
|
||||
#else
|
||||
static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
#endif
|
||||
@ -1303,7 +1295,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
|
||||
struct exfat_inode_info *new_ei = NULL;
|
||||
unsigned int new_entry_type = TYPE_UNUSED;
|
||||
int new_entry = 0;
|
||||
struct buffer_head *old_bh, *new_bh = NULL;
|
||||
struct buffer_head *new_bh = NULL;
|
||||
|
||||
/* check the validity of pointer parameters */
|
||||
if (new_path == NULL || strlen(new_path) == 0)
|
||||
@ -1314,16 +1306,11 @@ static int __exfat_rename(struct inode *old_parent_inode,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
exfat_chain_dup(&olddir, &ei->dir);
|
||||
exfat_chain_set(&olddir, EXFAT_I(old_parent_inode)->start_clu,
|
||||
EXFAT_B_TO_CLU_ROUND_UP(i_size_read(old_parent_inode), sbi),
|
||||
EXFAT_I(old_parent_inode)->flags);
|
||||
dentry = ei->entry;
|
||||
|
||||
ep = exfat_get_dentry(sb, &olddir, dentry, &old_bh);
|
||||
if (!ep) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
brelse(old_bh);
|
||||
|
||||
/* check whether new dir is existing directory and empty */
|
||||
if (new_inode) {
|
||||
ret = -EIO;
|
||||
@ -1425,10 +1412,17 @@ out:
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
static int exfat_rename(struct mnt_idmap *idmap,
|
||||
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 user_namespace *mnt_userns,
|
||||
struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
unsigned int flags)
|
||||
#endif
|
||||
#else
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
|
||||
static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
|
@ -513,7 +513,7 @@ static int exfat_utf8_to_utf16(struct super_block *sb,
|
||||
}
|
||||
|
||||
if (unilen > MAX_NAME_LENGTH) {
|
||||
exfat_err(sb, "failed to %s (estr:ENAMETOOLONG) nls len : %d, unilen : %d > %d",
|
||||
exfat_debug(sb, "failed to %s (estr:ENAMETOOLONG) nls len : %d, unilen : %d > %d",
|
||||
__func__, len, unilen, MAX_NAME_LENGTH);
|
||||
return -ENAMETOOLONG;
|
||||
}
|
||||
@ -679,7 +679,7 @@ static int exfat_load_upcase_table(struct super_block *sb,
|
||||
|
||||
bh = sb_bread(sb, sector);
|
||||
if (!bh) {
|
||||
exfat_err(sb, "failed to read sector(0x%llx)\n",
|
||||
exfat_err(sb, "failed to read sector(0x%llx)",
|
||||
(unsigned long long)sector);
|
||||
ret = -EIO;
|
||||
goto free_table;
|
||||
|
@ -547,7 +547,7 @@ static int parse_options(struct super_block *sb, char *options, int silent,
|
||||
break;
|
||||
default:
|
||||
if (!silent) {
|
||||
exfat_msg(sb, KERN_ERR,
|
||||
exfat_err(sb,
|
||||
"unrecognized mount option \"%s\" or missing value",
|
||||
p);
|
||||
}
|
||||
@ -559,14 +559,21 @@ out:
|
||||
if (opts->allow_utime == -1)
|
||||
opts->allow_utime = ~opts->fs_dmask & (0022);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
if (opts->discard && !bdev_max_discard_sectors(sb->s_bdev)) {
|
||||
exfat_warn(sb, "mounting with \"discard\" option, but the device does not support discard");
|
||||
opts->discard = 0;
|
||||
}
|
||||
#else
|
||||
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");
|
||||
if (!blk_queue_discard(q)) {
|
||||
exfat_warn(sb, "mounting with \"discard\" option, but the device does not support discard");
|
||||
opts->discard = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -625,8 +632,7 @@ static int exfat_read_root(struct inode *inode)
|
||||
inode->i_op = &exfat_dir_inode_operations;
|
||||
inode->i_fop = &exfat_dir_operations;
|
||||
|
||||
inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) &
|
||||
~((loff_t)sbi->cluster_size - 1)) >> inode->i_blkbits;
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
|
||||
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);
|
||||
@ -721,7 +727,7 @@ static int exfat_read_boot_sector(struct super_block *sb)
|
||||
*/
|
||||
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",
|
||||
exfat_err(sb, "bogus sector size bits : %u",
|
||||
p_boot->sect_size_bits);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -730,7 +736,7 @@ static int exfat_read_boot_sector(struct super_block *sb)
|
||||
* 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",
|
||||
exfat_err(sb, "bogus sectors bits per cluster : %u",
|
||||
p_boot->sect_per_clus_bits);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -896,6 +902,12 @@ static int exfat_fill_super(struct super_block *sb, void *data, int silent)
|
||||
if (opts->allow_utime == (unsigned short)-1)
|
||||
opts->allow_utime = ~opts->fs_dmask & 0022;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
if (opts->discard && !bdev_max_discard_sectors(sb->s_bdev)) {
|
||||
exfat_warn(sb, "mounting with \"discard\" option, but the device does not support discard");
|
||||
opts->discard = 0;
|
||||
}
|
||||
#else
|
||||
if (opts->discard) {
|
||||
struct request_queue *q = bdev_get_queue(sb->s_bdev);
|
||||
|
||||
@ -904,6 +916,7 @@ static int exfat_fill_super(struct super_block *sb, void *data, int silent)
|
||||
opts->discard = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
struct exfat_sb_info *sbi;
|
||||
|
||||
@ -924,7 +937,7 @@ static int exfat_fill_super(struct super_block *sb, void *data, int silent)
|
||||
DEFAULT_RATELIMIT_BURST);
|
||||
err = parse_options(sb, data, silent, &sbi->options);
|
||||
if (err) {
|
||||
exfat_msg(sb, KERN_ERR, "failed to parse options");
|
||||
exfat_err(sb, "failed to parse options");
|
||||
goto check_nls_io;
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user