diff --git a/fs/exfat/.github/workflows/c-cpp.yml b/fs/exfat/.github/workflows/c-cpp.yml new file mode 100644 index 000000000000..375c800ac23c --- /dev/null +++ b/fs/exfat/.github/workflows/c-cpp.yml @@ -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 diff --git a/fs/exfat/.travis_cmd_wrapper.pl b/fs/exfat/.travis_cmd_wrapper.pl deleted file mode 100755 index 0cbaea440eb9..000000000000 --- a/fs/exfat/.travis_cmd_wrapper.pl +++ /dev/null @@ -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]); diff --git a/fs/exfat/.travis_get_mainline_kernel b/fs/exfat/.travis_get_mainline_kernel deleted file mode 100755 index 3356d15da772..000000000000 --- a/fs/exfat/.travis_get_mainline_kernel +++ /dev/null @@ -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 diff --git a/fs/exfat/Kconfig b/fs/exfat/Kconfig index 2d3636dc5b8c..b3e5e9ebabc8 100644 --- a/fs/exfat/Kconfig +++ b/fs/exfat/Kconfig @@ -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. diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c index 30c2414d859c..c226d5d1c36b 100644 --- a/fs/exfat/balloc.c +++ b/fs/exfat/balloc.c @@ -12,6 +12,7 @@ #else #include #endif +#include #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) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 07765a74251a..bf8eb5d1b1c2 100644 --- a/fs/exfat/dir.c +++ b/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)) - 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) + break; + case ES_MODE_GET_BENIGN_SEC_ENTRY: + /* Assume unreconized benign secondary entry */ + if (!(type & TYPE_BENIGN_SEC)) 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; - } + 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; } diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 6b88270feaed..b79f2fe2bea5 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -10,6 +10,11 @@ #include #include #include +#include + +#ifndef SECTOR_SIZE +#define SECTOR_SIZE 512 +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) #include @@ -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, diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h index 7f39b1c6469c..0ece2e43cf49 100644 --- a/fs/exfat/exfat_raw.h +++ b/fs/exfat/exfat_raw.h @@ -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; diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c index b247cbdf987d..c13a04e94ebc 100644 --- a/fs/exfat/fatent.c +++ b/fs/exfat/fatent.c @@ -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,17 +359,11 @@ 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)", - hint_clu); + 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->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,8 +429,7 @@ 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; diff --git a/fs/exfat/file.c b/fs/exfat/file.c index ebea67c61da9..e35998f5aa8c 100644 --- a/fs/exfat/file.c +++ b/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) - 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; - } + /* + * 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; /* 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); - mark_inode_dirty(inode); - + } 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, }; diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index bd04c160848f..4d4449e1df3d 100644 --- a/fs/exfat/inode.c +++ b/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); } diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c index 1b75c8b97f7d..e7ae6f629d6e 100644 --- a/fs/exfat/misc.c +++ b/fs/exfat/misc.c @@ -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) diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index cae4ec4ac4d0..e6971c758892 100644 --- a/fs/exfat/namei.c +++ b/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, diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c index ef74f1639638..3b9fd4670aa2 100644 --- a/fs/exfat/nls.c +++ b/fs/exfat/nls.c @@ -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; diff --git a/fs/exfat/super.c b/fs/exfat/super.c index 6837e042be7f..665c296e4ad4 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -547,9 +547,9 @@ static int parse_options(struct super_block *sb, char *options, int silent, break; default: if (!silent) { - exfat_msg(sb, KERN_ERR, - "unrecognized mount option \"%s\" or missing value", - p); + exfat_err(sb, + "unrecognized mount option \"%s\" or missing value", + p); } return -EINVAL; } @@ -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"); - opts->discard = 0; + 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